1. Einführung
Im vorherigen Artikel wurde SignalR ausführlich vorgestellt und seine Anwendung in Asp.net MVC und WPF kurz vorgestellt. Im letzten Blogbeitrag wurde die Implementierung von Gruppennachrichten vorgestellt. Allerdings wurde SignalR für Echtzeit-Chat entwickelt, daher fehlt ihm im Gegensatz zu QQ natürlich der End-to-End-Chat. In diesem Blogbeitrag wird erläutert, wie Sie mit SignalR Funktionen implementieren, die dem QQ-Chat ähneln.
2. Die Idee, SignalR zur Implementierung von End-to-End-Chat zu verwenden
Bevor ich die spezifische Implementierung vorstelle, habe ich zunächst die Idee vorgestellt, SignalR zur Implementierung zu verwenden End-to-End-Chat. Ich glaube, Sie haben im vorherigen Artikel Code wie Clients.All.sendMessage(name, message); gesehen, was bedeutet, dass SendMessage für alle Clients aufgerufen wird. Der Hub von SignalR ermöglicht die Echtzeitkommunikation zwischen Clients und Servern. Um einen End-to-End-Chat zu erreichen, können Sie natürlich keine Nachrichten an alle Clients senden, sondern nur an bestimmte Clients. Andernfalls wird es chaotisch und es besteht keine Privatsphäre. Wie kann ich also eine Nachricht an einen bestimmten Kunden senden? Dieses Problem ist der Schlüssel zu unserer Implementierung der End-to-End-Chat-Funktion.
Zusätzlich zum All-Attribut verfügt das von uns gesendete Clients-Objekt auch über andere Attribute. Sie können in VS F12 drücken, um alle Attribute oder Methoden des Clients-Objekts anzuzeigen:
public interface IHubConnectionContext<T> { T All { get; } // 代表所有客户端 T AllExcept(params string[] excludeConnectionIds); // 除了参数中的所有客户端 T Client(string connectionId); // 特定的客户端,这个方法也就是我们实现端对端聊天的关键 T Clients(IList<string> connectionIds); // 参数中的客户端端 T Group(string groupName, params string[] excludeConnectionIds); // 指定客户端组,这个也是实现群聊的关键所在 T Groups(IList<string> groupNames, params string[] excludeConnectionIds); T User(string userId); // 特定的用户 T Users(IList<string> userIds); // 参数中的用户 }
Um seine Einzigartigkeit zu kennzeichnen, weist SignalR in SignalR jedem Client eine ConnnectionId zu, sodass wir den spezifischen Client über die ConnnectionId finden können. Auf diese Weise müssen wir beim Senden einer Nachricht an einen Client zusätzlich zur Weitergabe der Nachricht auch die an die andere Partei gesendete ConnectionId eingeben, damit der Server die entsprechende Nachricht basierend auf der Nachricht an den entsprechenden Client weiterleiten kann eingehende ConnectionId bereitgestellt. Damit ist die End-to-End-Chat-Funktion abgeschlossen. Wenn der Benutzer nicht online ist, kann der Server die Nachricht außerdem in der Datenbank speichern. Wenn der entsprechende Client online ist, kann er in der Datenbank prüfen, ob Nachrichten vorliegen, die übertragen werden müssen Daten aus der Datenbank übertragen und an den Client übertragen. (Die Funktion zum Zwischenspeichern von Daten auf der Serverseite ist in diesem Blogbeitrag jedoch nicht implementiert. Die Einführung hier dient dazu, jedem eines der Implementierungsprinzipien von QQ verständlich zu machen.)
Lassen Sie uns nun die Implementierungsideen der End-to-End-Chat-Funktion klären:
Wenn sich der Client anmeldet, notieren Sie die ConnectionId des Clients und fügen Sie diese Daten dem Benutzer hinzu dient zur Aufzeichnung aller Online-Benutzer.
Benutzer können auf den Benutzerchat unter Online-Benutzern klicken. Beim Senden einer Nachricht muss die ConnectionId an den Server übergeben werden.
Der Server ruft die Methode Clients.Client(connection).sendMessage basierend auf dem Inhalt der eingehenden Nachricht und der ConnectionId auf, um sie an den entsprechenden Client weiterzuleiten.
3. Implementieren Sie den Kerncode der coolen Chat-Funktion
Mit der Implementierungsidee wird es einfach sein, die Funktion zu implementieren am Hub ChatHub Code:
public class ChatHub : Hub { // 静态属性 public static List<UserInfo> OnlineUsers = new List<UserInfo>(); // 在线用户列表 /// <summary> /// 登录连线 /// </summary> /// <param name="userId">用户Id</param> /// <param name="userName">用户名</param> public void Connect(string userId, string userName) { var connnectId = Context.ConnectionId; if (OnlineUsers.Count(x => x.ConnectionId == connnectId) == 0) { if (OnlineUsers.Any(x => x.UserId == userId)) { var items = OnlineUsers.Where(x => x.UserId == userId).ToList(); foreach (var item in items) { Clients.AllExcept(connnectId).onUserDisconnected(item.ConnectionId, item.UserName); } OnlineUsers.RemoveAll(x => x.UserId == userId); } //添加在线人员 OnlineUsers.Add(new UserInfo { ConnectionId = connnectId, UserId = userId, UserName = userName, LastLoginTime = DateTime.Now }); } // 所有客户端同步在线用户 Clients.All.onConnected(connnectId, userName, OnlineUsers); } /// <summary> /// 发送私聊 /// </summary> /// <param name="toUserId">接收方用户连接ID</param> /// <param name="message">内容</param> public void SendPrivateMessage(string toUserId, string message) { var fromUserId = Context.ConnectionId; var toUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == toUserId); var fromUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == fromUserId); if (toUser != null && fromUser != null) { // send to Clients.Client(toUserId).receivePrivateMessage(fromUserId, fromUser.UserName, message); // send to caller user // Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message); } else { //表示对方不在线 Clients.Caller.absentSubscriber(); } } /// <summary> /// 断线时调用 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopCalled) { var user = OnlineUsers.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId); // 判断用户是否存在,存在则删除 if (user == null) return base.OnDisconnected(stopCalled); Clients.All.onUserDisconnected(user.ConnectionId, user.UserName); //调用客户端用户离线通知 // 删除用户 OnlineUsers.Remove(user); return base.OnDisconnected(stopCalled); } }
Das Obige ist die Hauptimplementierung des Servers. Schauen wir uns als Nächstes den Implementierungscode des Clients an:
<script type="text/javascript"> var systemHub = $.connection.chatHub; / 连接IM服务器成功 // 主要是更新在线用户 systemHub.client.onConnected = function (id, userName, allUsers) { var node = chatCore.node, myf = node.list.eq(0), str = '', i = 0; myf.addClass('loading'); onlinenum = allUsers.length; if (onlinenum > 0) { str += '<li class="ChatCore_parentnode ChatCore_liston">' + '<h5><i></i><span class="ChatCore_parentname">在线用户</span><em class="ChatCore_nums">(' + onlinenum + ')</em></h5>' + '<ul id="ChatCore_friend_list" class="ChatCore_chatlist">'; for (; i < onlinenum; i++) { str += '<li id="userid-' + allUsers[i].UserID + '" data-id="' + allUsers[i].ConnectionId + '" class="ChatCore_childnode" type="one"><img src="/Content/Images/001.jpg?' + allUsers[i].UserID + '" class="ChatCore_oneface" alt="Asp.net verwendet SignalR, um eine coole End-to-End-Chat-Funktion zu implementieren" ><span class="ChatCore_onename">' + allUsers[i].UserName + '(' + ')</span><em class="ChatCore_time">' + allUsers[i].LoginTime + '</em></li>'; } str += '</ul></li>'; myf.html(str); } else { myf.html('<li class="ChatCore_errormsg">没有任何数据</li>'); } myf.removeClass('loading'); }; //消息传输 chatCore.transmit = function () { var node = chatCore.node, log = {}; node.sendbtn = $('#ChatCore_sendbtn'); node.imwrite = $('#ChatCore_write'); //发送 log.send = function () { var data = { content: node.imwrite.val(), id: chatCore.nowchat.id, sign_key: '', //密匙 _: +new Date }; if (data.content.replace(/\s/g, '') === '') { layer.tips('说点啥呗!', '#ChatCore_write', 2); node.imwrite.focus(); } else { //此处皆为模拟 var keys = chatCore.nowchat.type + chatCore.nowchat.id; //聊天模版 log.html = function (param, type) { return '<li class="' + (type === 'me' ? 'ChatCore_chateme' : '') + '">' + '<div class="ChatCore_chatuser">' + function () { if (type === 'me') { return '<span class="ChatCore_chattime">' + param.time + '</span>' + '<span class="ChatCore_chatname">' + param.name + '</span>' + '<img src="' + param.face + '" alt="Asp.net verwendet SignalR, um eine coole End-to-End-Chat-Funktion zu implementieren" >'; } else { return '<img src="' + param.face + '" alt="Asp.net verwendet SignalR, um eine coole End-to-End-Chat-Funktion zu implementieren" >' + '<span class="ChatCore_chatname">' + param.name + '</span>' + '<span class="ChatCore_chattime">' + param.time + '</span>'; } }() + '</div>' + '<div class="ChatCore_chatsay">' + param.content + '<em class="ChatCore_zero"></em></div>' + '</li>'; }; log.imarea = chatCore.chatbox.find('#ChatCore_area' + keys); log.imarea.append(log.html({ time: new Date().toLocaleString(), name: config.user.name, face: config.user.face, content: data.content }, 'me')); node.imwrite.val('').focus(); log.imarea.scrollTop(log.imarea[0].scrollHeight); // 调用服务端sendPrivateMessage方法来转发消息 systemHub.server.sendPrivateMessage(chatCore.nowchat.id, data.content); } }; node.sendbtn.on('click', log.send); node.imwrite.keyup(function (e) { if (e.keyCode === 13) { log.send(); } }); }; //用户离线 systemHub.client.onUserDisconnected = function (id, userName) { onlinenum = onlinenum - 1; $(".ChatCore_nums").html("(" + onlinenum + ")"); $("#ChatCore_friend_list li[data-id=" + id + "]").remove(); }; // 启动连接 $.connection.hub.start().done(function () { systemHub.server.connect(userid, username); // 调用服务端connect方法 }); </script>
Oben sind nur einige Kerncode-Implementierungen aufgeführt. Um coole Effekte zu erzielen, wird hier außerdem ein JQuery-Plugin verwendet: Layer Die offizielle Website ist: http://layer.layui.com/. Dieses Plug-In wird hauptsächlich verwendet, um die Effekte von Popup-Boxen und Popup-Ebenen zu erzielen. Um coole Chat-Effekte zu erzielen, müssen Sie selbst JS-Code schreiben, da ich mit dem Frontend nicht sehr vertraut bin Der Code für Spezialeffekte basiert ebenfalls auf der Implementierung im Internet. Wenn Sie es ausführen und die Wirkung sehen möchten, wird empfohlen, den Quellcode am Ende des Artikels herunterzuladen und auszuführen.
4. Endeffekt
Nachdem wir die Implementierungsideen und den Implementierungscode vorgestellt haben, sind wir nun an unserem spannenden Moment angelangt, nämlich zu sehen, ob unsere implementierten Funktionen die Anforderungen erfüllen können. Darüber hinaus müssen Sie nicht nur die grundlegenden Chat-Funktionen erfüllen, sondern auch prüfen, ob die Benutzeroberfläche cool genug ist.
Weitere Artikel im Zusammenhang mit Asp.net, das SignalR verwendet, um coole End-to-End-Lösungen zu implementieren Chat-Funktion Bitte achten Sie auf die chinesische PHP-Website!