1、概述 通过前面几篇文章对SignalR的详细介绍。我们知道Asp.net SignalR是微软为实现实时通信的一个类库。一般情况下,SignalR会使用JavaScript的长轮询(long polling)的方式来实现客户端和服务器通信,随着Html5中WebSockets出现,SignalR也支持WebSockets通信。另外SignalR开发的程序不仅仅限制于宿主在IIS中,也可以宿主在任何应用程序,包括控制台,客户端程序和Windows服务等,另外还支持Mono,这意味着它可以实现跨平台部署在Linux环境下。 SignalR内部有两类对象: Http持久连接(Persisten Connection)对象:用来解决长时间连接的功能。还可以由客户端主动向服务器要求数据,而服务器端不需要实现太多细节,只需要处理PersistentConnection 内所提供的五个事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。 Hub(集线器)对象:用来解决实时(realtime)信息交换的功能,服务端可以利用URL来注册一个或多个Hub,只要连接到这个Hub,就能与所有的客户端共享发送到服务器上的信息,同时服务端可以调用客户端的脚本。 SignalR将整个信息的交换封装起来,客户端和服务器都是使用JSON来沟通的,在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,.NET则依赖Proxy来生成代理对象,而Proxy的内部则是将JSON转换成对象。 2、SignalR实现聊天室(群聊)功能 要想实现群聊的功能,首先我们需要创建一个房间,然后每个在线用户可以加入这个房间里面进行群聊,我们可以为房间设置一个唯一的名字来作为标识。那SignalR类库里面是否有这样现有的方法呢?答案是肯定的。SignalR作为一个强大的集线器,已经在hub里面集成了Gorups,也就是分组管理。 // IGroupManager接口提供如下方法 // 作用:将连接ID加入某个组 // Context.ConnectionId 连接ID,每个页面连接集线器即会产生唯一ID // roomName分组的名称 Groups.Add(Context.ConnectionId, roomName); // 作用:将连接ID从某个分组移除 Groups.Remove(Context.ConnectionId, roomName); // IHubConnectionContext接口提供了如下方法 // 调用客户端方法向房间内所有用户群发消息 // Room:分组名称 // new string[0]:过滤(不发送)的连接ID数组 Clients.Group(Room, new string[0]).clientMethod 上面的代码就是实现群聊的核心方法。Groups对象就是SignalR类库维护的一个列表对象而已,我们完全可以自己维护一个Dictionary对象,创建一个房间的时候,我们将房间名称和进入房间的客户端的ConnectionId加入到这个字典里面,然后在聊天室里面点发送消息的时候,我们根据房间名查找到所有加入群聊的ConnectionId,然后调用Clients.Clients(IList connectionIds)方法来将消息群发到每个客户端。以上也就是实现聊天室的原理。 2.1、 创建ASP.NET Mvc项目 新建一个空的ASP.NET Mvc项目,取名为:SignalRGroupChat。 2.2、安装Nuget包 创建好项目后,要使用SignalR,需要先安装SignalR包,可以通过程序包管理控制台输入包安装命令进行安装。 Install-Package Microsoft.AspNet.SignalR Install-Package Microsoft.Owin.Cors 2.3、聊天室后台代码实现 要实现聊天室功能,我们需要一些基础实体,如:用户类、房间类等,直接上代码: using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace SignalRGroupChat { public class UserContext { public UserContext() { Users = new List(); Connections = new List(); Rooms = new List(); } /// /// 用户集合 /// public List Users { get; set; } /// /// 连接集合 /// public List Connections { get; set; } /// /// 房间集合 /// public List Rooms { get; set; } } public class User { /// /// 用户名 /// [Key] public string UserName { get; set; } /// /// 用户的连接 /// public List Connections { get; set; } /// /// 用户房间集合 /// public virtual List Rooms { get; set; } public User() { Connections = new List(); Rooms = new List(); } } public class Connection { /// /// 连接ID /// public string ConnectionID { get; set; } /// /// 用户代理 /// public string UserAgent { get; set; } /// /// 是否连接 /// public bool Connected { get; set; } } /// /// 房间类 /// public class ConversationRoom { /// /// 房间名称 /// [Key] public string RoomName { get; set; } /// /// 用户集合 /// public virtual List Users { get; set; } public ConversationRoom() { Users = new List(); } } } 实现聊天室的SignalR Hub代码: using Microsoft.AspNet.SignalR; using Microsoft.AspNet.SignalR.Hubs; using Newtonsoft.Json; using System; using System.Linq; using System.Threading.Tasks; namespace SignalRGroupChat.Hubs { /// /// 聊天室(群聊) /// [HubName("groupHub")] public class GroupHub : Hub { public static UserContext db = new UserContext(); public void Hello() { Clients.All.hello(); } /// /// 重写Hub连接事件 /// /// public override Task OnConnected() { // 查询用户。 var user = db.Users.SingleOrDefault(u => u.UserName == Context.ConnectionId); //判断用户是否存在,否则添加 if (user == null) { user = new User() { UserName = Context.ConnectionId }; db.Users.Add(user); } //发送房间列表 var itme = from a in db.Rooms select new { a.RoomName }; Clients.Client(this.Context.ConnectionId).getRoomlist(JsonConvert.SerializeObject(itme.ToList())); return base.OnConnected(); } /// /// 更新所有用户的房间列表 /// private void GetRoomList() { var itme = from a in db.Rooms select new { a.RoomName }; string jsondata = JsonConvert.SerializeObject(itme.ToList()); Clients.All.getRoomlist(jsondata); } // 重写Hub连接断开的事件 public override Task OnDisconnected(bool stopCalled) { // 查询用户 var user = db.Users.FirstOrDefault(u => u.UserName == Context.ConnectionId); if (user != null) { // 删除用户 db.Users.Remove(user); // 从房间中移除用户 foreach (var item in user.Rooms) { RemoveFromRoom(item.RoomName); } } return base.OnDisconnected(stopCalled); } /// /// 加入聊天室 /// /// public void AddToRoom(string roomName) { //查询聊天室 var room = db.Rooms.Find(a => a.RoomName == roomName); //存在则加入 if (room != null) { //查找房间中是否存在此用户 var isuser = room.Users.Where(a => a.UserName == Context.ConnectionId).FirstOrDefault(); //不存在则加入 if (isuser == null) { var user = db.Users.Find(a => a.UserName == Context.ConnectionId); user.Rooms.Add(room); room.Users.Add(user); Groups.Add(Context.ConnectionId, roomName); //调用此连接用户的本地JS(显示房间) Clients.Client(Context.ConnectionId).addRoom(roomName); } else { Clients.Client(Context.ConnectionId).showMessage("请勿重复加入房间!"); } } } /// /// 创建聊天室 /// /// public void CreatRoom(string roomName) { var room = db.Rooms.Find(a => a.RoomName == roomName); if (room == null) { ConversationRoom cr = new ConversationRoom() { RoomName = roomName }; //将房间加入列表 db.Rooms.Add(cr); AddToRoom(roomName); Clients.Client(Context.ConnectionId).showMessage("房间创建完成!"); GetRoomList(); } else { Clients.Client(Context.ConnectionId).showMessage("房间名重复!"); } } /// /// 退出聊天室 /// /// public void RemoveFromRoom(string roomName) { //查找房间是否存在 var room = db.Rooms.Find(a => a.RoomName == roomName); //存在则进入删除 if (room != null) { //查找要删除的用户 var user = room.Users.Where(a => a.UserName == Context.ConnectionId).FirstOrDefault(); //移除此用户 room.Users.Remove(user); //如果房间人数为0,则删除房间 if (room.Users.Count <= 0) { db.Rooms.Remove(room); } Groups.Remove(Context.ConnectionId, roomName); //提示客户端 Clients.Client(Context.ConnectionId).removeRoom("退出成功!"); } } /// /// 给分组内所有的用户发送消息 /// /// 分组名 /// 信息 public void SendMessage(string Room, string Message) { Clients.Group(Room, new string[0]).sendMessage(Room, Message + " " + DateTime.Now.ToString("HH:mm:ss")); } } } 2.4、页面部分代码参考 @{ ViewBag.Title = "GroupChat"; }

聊天室(群聊)实例

当前用户:
输入房间名:
房间列表

    @section scripts { } 3、效果展示 效果展示 4、代码下载 实例源码可以移步github下载,地址:https://github.com/yonghu86/SignalRTestProj 5、参考文章 RDIFramework.NET敏捷开发框架通过SignalR技术整合即时通讯(IM) 史上最全面的SignalR系列教程-1、认识SignalR 史上最全面的SignalR系列教程-2、SignalR 实现推送功能-永久连接类实现方式 史上最全面的SignalR系列教程-3、SignalR 实现推送功能-集线器类实现方式 史上最全面的SignalR系列教程-4、SignalR 自托管全解(使用Self-Host)-附各终端详细实例 史上最全面的SignalR系列教程-5、SignalR 实现一对一聊天 Real-time ASP.NET with SignalR 微信公众号开发系列-玩转微信开发-目录汇总 R