SignalR学习笔记(一) 简单聊天室
什么是ASP.NET SignalR?
ASP.NET SignalR是一个方便程序员添加实时网络通信功能的类库。所谓的实时网络通信功能(Real-time Web Functionality)就是需要服务器主动推送数据到用户客户端,而非服务器等待用户客户端请求数据的功能。以聊天室为例,当一个用户发送群发消息之后,在所有用户的聊天窗口中都需要显示出这条消息,如果每个用户客户端都是用Ajax每隔一定时间去服务器上拉取消息,那样不仅效率低下并增加服务器负担,也不是真正意义上的实时程序。
消息传输机制
ASP.Net SignalR使用新的Websocket传输协议,他实现了浏览器和服务器全双工通信(允许服务器主动发消息给客户端),并兼容以前的长连接(Long poll)传输方式, 所以对于开发人员来说,不需要自己去维护使用何种传输方式,SignalR会自动根据客户端浏览器的版本自动切换消息传输方式
- 对于IE8,会自动切换到Long poll传输。
- 对于IE9及以上版本,会使用Websocket传输。
连接与枢纽
SignalR API包含2个模型来帮助客户端与服务器进行通信: 持久连接(Persistent Connections)与枢纽 (hubs).
持久连接负责传输消息,枢纽提供了一种很有趣的机制,允许开发人员在客户端调用服务器端方法或服务器端调用客户端方法。
枢纽(Hubs)是如何工作的
当服务器端程序调用客户端方法时,一个包含客户端方法名及参数的Json数据包会通过持久连接传输到客户端,客户端会根据数据包中的方法名进行匹配,如果找到相同方法名的方法,就会自动调用找到的方法,方法所需的参数值会使用数据包中对应的参数值。
简单的聊天室
下面用一个聊天室的例子,熟悉一下SignalR的基本使用方式。
这个聊天室页面的需求如下:
- 用户打开聊天室页面,需要首先录入自己的昵称,输入昵称之后,通知聊天室的其他用户该用户进入聊天室
- 用户可以使用在聊天室中发送公共消息
- 用户可以使用“to 用户名 消息内容”的方式发送私聊消息
添加项目
新建一个空的ASP.NET Web Application工程
通过Nuget或者最新版本的SignalR库
展开Package Manager Console面板,输入
install-package Microsoft.AspNet.SignalR
添加Startup.cs启用SignalR
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(ChatRoom.Startup))] namespace ChatRoom { public class Startup { public void Configuration(IAppBuilder app) { //启用SignalR app.MapSignalR(); } } }
添加枢纽(Hub)
在工程中添加ChatRoomHub.cs, 选择SignalR Hub Class(v2)
替换ChatRoomHub类中的内容替换
using Microsoft.AspNet.SignalR; using System.Collections.Generic; using System.Linq; namespace ChatRoom { public class ChatRoomHub : Hub { private static Dictionary<string, string> _nickNames = new Dictionary<string, string>(); public void SetNickName(string nickName) { //当Hub启动完毕,每个连接到这个Hub的客户端都会自动分配一个唯一的ConnectionId。 //当SignalR向指定客户端推送消息的时候,需要指定ConnectionId, 所以这里需要记录一下每个昵称对应的客户端ConnectionId _nickNames.Add(Context.ConnectionId, nickName); //当用户设置昵称之后,需要发送欢迎信息到所有的用户客户端,调用客户端receiveWelcomeMessage方法显示欢迎信息 Clients.All.ReceiveWelcomeMessage($"{nickName}进入聊天室。"); } public void Send(string nickName, string message) { if (string.IsNullOrWhiteSpace(nickName) || string.IsNullOrWhiteSpace(message)) { //如果用户昵称或者消息不存在,就不做任何操作 return; } if (message.StartsWith("to") && message.Split(' ').Length == 3) { //私聊消息 var toUserName = message.Split(' ')[1]; if (_nickNames.ContainsValue(toUserName)) { var connectionId = _nickNames.First(p => p.Value == toUserName).Key; if (!string.IsNullOrWhiteSpace(connectionId) && connectionId != Context.ConnectionId) { Clients.Client(connectionId).ReceivePrivateMessage(nickName, message.Split(' ')[2]); } } } else { //普通广播消息 if (_nickNames.ContainsValue(nickName)) { Clients.All.ReceiveBroadcastMessage(nickName, message); } } } } }
添加显示页面
添加空html文件ChatRoom.html, 使用以下文件替换
<!DOCTYPE html> <html> <head> <title>SignalR Simple Chat</title> <style type="text/css"> .container { background-color: #99CCFF; border: thick solid #808080; padding: 20px; margin: 20px; } </style> </head> <body> <div class="container"> <input type="text" id="message" /> <input type="button" id="sendmessage" value="Send" /> <input type="hidden" id="displayname" /> <ul id="discussion"></ul> </div> <script src="Scripts/jquery-1.6.4.min.js"></script> <script src="Scripts/jquery.signalR-2.2.2.min.js"></script> <script src="signalr/hubs"></script> <script type="text/javascript"> $(function () { //使用代理模式, 创建客户端的hub代理 var chat = $.connection.chatRoomHub; //服务器Hub中, 调用ReceiveWelcomeMessage方法时, 会执行客户端的chat.client.receiveBroadcastMessage方法 chat.client.receiveBroadcastMessage = function (name, message) { //防JS输入 var encodedName = $('<div />').text(name).html(); var encodedMsg = $('<div />').text(message).html(); $('#discussion').append('<li><strong>' + encodedName + '</strong>: ' + encodedMsg + '</li>'); }; //服务器Hub中, 调用ReceiveWelcomeMessage方法时, 会执行客户端的chat.client.receiveWelcomeMessage方法 chat.client.receiveWelcomeMessage = function (message) { var encodedMsg = $('<div />').text(message).html(); $('#discussion').append('<li><strong style="color:blue">' + encodedMsg + '</strong></li>'); }; //服务器Hub中, 调用ReceivePrivateMessage方法时, 会执行客户端的chat.client.receivePrivateMessage方法 chat.client.receivePrivateMessage = function (name, message) { var encodedName = $('<div />').text(name).html(); var encodedMsg = $('<div />').text(message).html(); $('#discussion').append('<li><strong style="color: green">' + encodedName + '偷偷的跟你说</strong>: ' + encodedMsg + '</li>'); }; //通过代理连接到服务器Hub $.connection.hub.start().done(function () { $('#sendmessage').click(function () { chat.server.send($('#displayname').val(), $('#message').val()); $('#message').val('').focus(); }); //连接成功后, 需要用户立刻输入昵称 $('#displayname').val(prompt('Enter your name:', '')); chat.server.setNickName($('#displayname').val()); $('#message').focus(); }); }); </script> </body> </html>
最终实现效果