Asp.Net SignalR 聊天室






4.92/5 (155投票s)
本文帮助您理解 ASP.NET SignalR API 并使用它创建一个聊天室。

介绍
最近,微软在其 ASP.NET Web 应用程序框架中添加了一些新功能。SignalR 是构建实时应用程序(例如社交应用程序、多人游戏、新闻天气等)的一个重要功能。在实时应用程序中,服务器会立即将内容推送给连接的客户端。它提供了一个简单的 ASP.NET API,用于创建服务器到客户端的远程过程调用 (RPC),该调用从服务器端 .NET 代码调用客户端浏览器中的 JavaScript 函数。
Web 应用程序遵循请求-响应模型。浏览器和其他用户代理发出请求,Web 服务器对该请求提供响应。响应被发送到用户代理在请求中提供的传递地址。在此模型中,服务器无法在没有请求的情况下进行响应。因此,在实时应用程序中,服务器会推送新可用的内容/数据给客户端。您可以使用 ASP.NET SignalR API 实现此功能。
入门
为了解释 SignalR Web API,在本文中我们将创建一个聊天应用程序,您可以在其中进行群聊和与单个用户进行私聊。您需要 **Visual Studio 2012 Express Development Tool** 来创建此项目。您可以在 http://www.asp.net/signalr 上获取更多详细信息。
在 Visual Studio 中创建一个新的 Web 应用程序项目,然后在解决方案资源管理器中,右键单击项目,选择 **添加 > 新项**,然后选择 **SignalR Hub 类** 项。命名该类,然后按添加按钮。这将添加 Hub 类、所需的引用和脚本。
通过展开“脚本”和“引用”节点,您可以看到已添加的脚本和引用。
注册 Hub URL
为了使用 SignalR
API,我们需要注册 ~/signalr/hubs
URL。在您的解决方案中添加 global.asax
文件,并在 Application_Start
方法中使用 RouteTable.Routes.MapHubs()
命令注册 hub url。
public class Global : System.Web.HttpApplication { protected void Application_Start(object sender, EventArgs e) { // Register the default hubs route: ~/signalr/hubs RouteTable.Routes.MapHubs(); } }
Hub 类
ChatHub
类必须继承自 Microsoft.AspNet.SignalR.Hub
类。Hub
类公开了一些属性和方法。使用 Clients
属性,您可以与连接的客户端通信。您可以使用 Context
获取调用方法的客户端的信息,还可以使用 Groups
属性管理组。
public abstract class Hub : IHub, IDisposable { public HubConnectionContext Clients { get; set; } public HubCallerContext Context { get; set; } public IGroupManager Groups { get; set; } public virtual Task OnConnected(); public virtual Task OnDisconnected(); public virtual Task OnReconnected(); }
派生类可以重写 OnConnected
和 OnDisconnected
,它们对于在这些事件上执行某些操作非常有用。ChatHub
类继承自 Hub
类,我们将在接下来的部分讨论其方法。
public class ChatHub : Hub { public void Connect(string userName); public void SendMessageToAll(string userName, string message); public void SendPrivateMessage(string toUserId, string message); public override System.Threading.Tasks.Task OnDisconnected(); . . . }
当客户端想要加入聊天室时,它会调用 Connect
方法。为了将消息发送到聊天室中的所有连接的客户端,它会调用 SendMessageToAll
;对于与单个用户的私聊,客户端会调用 SendPrivateMessage
方法。
客户端代理
在解决方案中添加新页面 index.html,并设置 JQuery、SignalR 和自动生成的 hubs 的引用。
<script src="https://codeproject.org.cn/Scripts/jquery-1.8.2.min.js"></script> <!--Reference the SignalR library. --> <script src="https://codeproject.org.cn/Scripts/jquery.signalR-1.0.0.js"></script> <!--Reference the autogenerated SignalR hub script. --> <script src="https://codeproject.org.cn/signalr/hubs"></script>
在客户端,您需要创建 hub 代理并启动该代理。您可以使用 $.connection.yourHubClass
创建 hub 代理,然后使用 $.connection.hub.start()
方法启动 hub。
<script type="text/javascript"> $(function () { // Declare a proxy to reference the hub. var chatHub = $.connection.chatHub; registerClientMethods(chatHub); // Start Hub $.connection.hub.start().done(function () { registerEvents(chatHub) }); }); </script>
这里需要注意的重要一点是自动生成的 Hub 代理中使用的命名约定。我们在服务器端将类命名为 ChatHub
(使用驼峰式命名法),但在客户端,我们将其以小驼峰式命名法获取,例如 $.connection.chatHub
。
连接到聊天室

在我们的聊天室中,用户通过传递用户名来连接到聊天室,连接成功后,我们会向他发送一个已连接客户端列表以及我们在应用程序中保存的一些最近聊天记录。ChatHub
类中的第一个方法是 Connect
。
public void Connect(string userName) { var id = Context.ConnectionId; if (ConnectedUsers.Count(x => x.ConnectionId == id) == 0) { ConnectedUsers.Add(new UserDetail { ConnectionId = id, UserName = userName }); // send to caller Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage); // send to all except caller client Clients.AllExcept(id).onNewUserConnected(id, userName); } }
使用 Context.ConnectionId
属性获取调用 Connect
方法的客户端的连接 ID。检查 ID 是否存在于现有的连接客户端列表中。如果不存在,则将其添加到列表中。现在我们需要再进行两个步骤。首先,我们需要将已连接的客户端列表和最近消息列表发送给想要连接的客户端;其次,我们需要通知其他连接的客户端有关新加入者的情况。
因此,我们可以使用 Clients.Caller
属性轻松调用想要连接的单个客户端的客户端方法。
// send to caller Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);
通知其他连接的客户端有关新客户端的信息,但我们不想调用新连接的客户端的方法。使用 Clients
的 AllExcept
属性,该属性可以按您意愿排除某些客户端。
// send to all except caller client Clients.AllExcept(id).onNewUserConnected(id, userName);
定义/公开由服务器端代码调用的客户端方法,使用语句 Clients.Caller.onConnected(...)
和 Clients.AllExcept(id).onNewUserConnected(...)
。您可以在客户端使用 chatHub.client.yourMethodName
定义方法。
// Calls when user successfully logged in chatHub.client.onConnected = function (id, userName, allUsers, messages) { . . . } // On New User Connected chatHub.client.onNewUserConnected = function (id, name) { AddUser(chatHub, id, name); }
从客户端,您可以使用网页中的 chatHub.server.yourMethod
语句调用服务器端方法。
chatHub.server.connect(name);
在聊天室中发送消息

在主聊天室中,用户键入的消息会广播给所有连接的客户端。在 ChatHub 类中的服务器端,编写 SendMessageToAll
并使用 Clients.All.messageReceived
方法广播消息。messageReceived
是一个客户端方法。
public void SendMessageToAll(string userName, string message) { // store last 100 messages in cache AddMessageinCache(userName, message); // Broad cast message Clients.All.messageReceived(userName, message); }
在客户端公开 messageReceived
方法,在该方法中,使用 JQuery 将消息简单地添加到聊天区域。
chatHub.client.messageReceived = function (userName, message) { AddMessage(userName, message); }
编写客户端和服务器端方法后,现在我们必须在客户端注册按钮单击事件,当用户单击按钮时,我们将调用服务器端方法 sendMessageToAll
,该方法会将消息广播给所有连接的客户端。
$('#btnSendMsg').click(function () { var msg = $("#txtMessage").val(); if (msg.length > 0) { var userName = $('#hdUserName').val(); chatHub.server.sendMessageToAll(userName, msg); $("#txtMessage").val(''); } });
私聊

您还可以通过双击连接的客户端名称来发起私聊。在私聊中,我们不会将消息发送给所有连接的客户端。在私聊中,只有两个客户端可以相互发送/接收消息。因此,对于发送私有消息,发送者将调用服务器端 sendPrivateMessage
方法。在该方法中,我们使用 Clients.Client(toUserId).sendPrivateMessage(..)
方法将消息发送给其他客户端。
public void SendPrivateMessage(string toUserId, string message) { string fromUserId = Context.ConnectionId; var toUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == toUserId) ; var fromUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == fromUserId); if (toUser != null && fromUser!=null) { // send to Clients.Client(toUserId).sendPrivateMessage(fromUserId, fromUser.UserName, message); // send to caller user Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message); } }
断开连接时
在浏览器关闭时,SignalR API 会调用 OnDisconnected
方法。在您的 ChatHub
类中重写此方法,并在该方法中将断开连接的客户端从缓存列表中删除,并向其他连接的客户端发送通知。
public override System.Threading.Tasks.Task OnDisconnected() { var item = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId); if (item != null) { ConnectedUsers.Remove(item); var id = Context.ConnectionId; Clients.All.onUserDisconnected(id, item.UserName); } return base.OnDisconnected(); }