SignalR实现消息推送,包括私聊、群聊、在线所有人接收消息(源码)
一、关于SignalR
1、简介:Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架。它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请求或使用轮询技术来获取消息。
可访问其官方网站:https://github.com/SignalR/ 获取更多资讯。
2、SignalR 的实现机制与 .NET WCF 或 Remoting 是相似的,都是使用远程代理来实现。在具体使用上,有两种不同目的的接口:PersistentConnection 和 Hubs,其中 PersistentConnection 是实现了长时间的 Javascript 轮询(类似于 Comet),Hub 是用来解决实时信息交换问题,它是利用 Javascript 动态载入执行方法实现的。SignalR 将整个连接,信息交换过程封装得非常漂亮,客户端与服务器端全部使用 JSON 来交换数据。
下面就 Hubs 接口的使用来讲讲整个流程:
(1),在服务器端定义对应的 hub class;
(2),在客户端定义 hub class 所对应的 proxy 类;
(3),在客户端与服务器端建立连接(connection);
(4),然后客户端就可以调用 proxy 对象的方法来调用服务器端的方法,也就是发送 request 给服务器端;
(5),服务器端接收到 request 之后,可以针对某个/组客户端或所有客户端(广播)发送消息。
以上这些都是关注大神们了解的。
二、具体使用
1、建立一个mvc项目 SignalR通讯
2、安装SignalR
(1)、在SignalR通讯 项目下安装 SignalR(找到程序包管理器控制台,输入:Install-Package Microsoft.AspNet.SignalR)
安装成功后系统会自动生成一个Scripts文件夹,里面存放对应的js文件,如图
3、安装好环境以后。那么接下来我们就开始搭建我们的环境及编写相应的代码
(1)、新建一个SignalR集线器ServerHub,如图
并编写以下代码
1 public class ServerHub : Hub 2 { 3 private static readonly char[] str = 4 { 5 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 6 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 7 'w', 'x', 'y', 'z', 8 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 9 'W', 'X', 'Y', 'Z' 10 }; 11 12 /// <summary> 13 /// 消息发送接口 14 /// </summary> 15 /// <param name="message"></param> 16 public void SendMsg(string message) 17 { 18 var name = GenerateUserName(4); 19 20 // 调用所有客户端的sendMessage方法 21 Clients.All.sendMessage(name, message); 22 } 23 24 /// <summary> 25 /// 产生随机用户 26 /// </summary> 27 /// <param name="length">用户名长度</param> 28 /// <returns></returns> 29 public static string GenerateUserName(int length) 30 { 31 var newRandom = new StringBuilder(62); 32 var rd = new Random(); 33 for (var i = 0; i < length; i++) 34 { 35 newRandom.Append(str[rd.Next(62)]); 36 } 37 return newRandom.ToString(); 38 }
(2)、创建一个Startup类,(如果创建项目是有这个类。就不用添加了)添加代码如下
public void Configuration(IAppBuilder app) { // 配置集线器 app.MapSignalR(); }
(3)、添加Home控制器及视图Index(此步骤不截图)
(4)、添加Index视图代码 如下
1 @{ 2 Layout = "~/Views/Shared/_Layout.cshtml"; 3 ViewBag.Title = "聊天窗口"; 4 } 5 6 <h2>Index</h2> 7 8 <div class="container"> 9 <input type="text" id="message" /> 10 <input type="button" id="sendmessage" value="Send" /> 11 <input type="hidden" id="displayname" /> 12 <ul id="discussion"></ul> 13 </div> 14 15 @section scripts 16 { 17 <script src="~/Scripts/jquery-1.6.4.min.js"></script> 18 <!--引用SignalR库. --> 19 <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script> 20 <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 --> 21 <script src="~/signalr/hubs"></script> 22 23 <script> 24 $(function () { 25 // 引用集线器代理 26 var chat = $.connection.serverHub; 27 // 定义服务器端调用的客户端sendMessage来显示新消息 28 29 chat.client.sendMessage = function (name, message) { 30 // 向页面添加消息 31 $('#discussion').append('<li><strong>' + htmlEncode(name) 32 + '</strong>: ' + htmlEncode(message) + '</li>'); 33 }; 34 35 // 设置焦点到输入框 36 $('#message').focus(); 37 // 开始连接服务器 38 $.connection.hub.start().done(function () { 39 $('#sendmessage').click(function () { 40 // 调用服务器端集线器的SendMsg方法 41 chat.server.sendMsg($('#message').val()); 42 // 清空输入框信息并获取焦点 43 $('#message').val('').focus(); 44 }); 45 }); 46 }); 47 48 // 为显示的消息进行Html编码 49 function htmlEncode(value) { 50 var encodedValue = $('<div />').text(value).html(); 51 return encodedValue; 52 } 53 </script> 54 }
4、完成以上步骤之后,我们的SignalR算是简单的完成。让我们来看下制作结果吧
(1)、如果出现如下错误,请删除红框里的代码
成功之后的结果,如图
你以为完了嘛?还没有,别急往下看
三、页面有优化及私聊、群聊、在线所有人接收消息的实现
1、发送所有在线人员
(1)、在layer官网下载layer前端js和ui(在我上一篇的文章中有下载地址,或者直接百度搜索layer),放到你的项目中,在这里我们就不直接说页面代码的编写部分,直接看下我们的效果图,如下
(2)、在完成聊天之前。我们还需要做什么,对做登录,在这里自动生成的用户已经不能满足我们接下来的需求了(登录教程跳过)直接看登录成功过后的界面
这样看起来就美观多了,其实在线所有人聊天的功能,就是刚刚我们实现那个功能是一样的,没有任何的变化(只是美观了下),看看聊天结果吧
2、群聊
(1)、首先我们的建一个群聊的实体(UserGroup)和房间实体(ChatRoom),如下图
(2)、在我们建好的ServerHub类里编写如下代码(具体实现看源码)
1 public static ChatContext DbContext = new ChatContext(); 2 3 // 重写Hub连接断开的事件 (断线时调用) 4 public override Task OnDisconnected(bool stopCalled) 5 { 6 // 查询用户 7 var user = DbContext.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId); 8 9 if (user != null) 10 { 11 // 删除用户 12 DbContext.Users.Remove(user); 13 14 // 从房间中移除用户 15 foreach (var item in user.Rooms) 16 { 17 RemoveUserFromRoom(item.RoomName); 18 } 19 } 20 return base.OnDisconnected(stopCalled); 21 } 22 23 // 为所有用户更新房间列表 24 public void UpdateRoomList() 25 { 26 var itme = DbContext.Rooms.Select(p => new { p.RoomName }); 27 var jsondata = JsonHelper.ToJsonString(itme.ToList()); 28 Clients.All.getRoomlist(jsondata); 29 } 30 31 /// <summary> 32 /// 加入聊天室 33 /// </summary> 34 public void JoinRoom(string roomName) 35 { 36 // 查询聊天室 37 var room = DbContext.Rooms.Find(p => p.RoomName == roomName); 38 39 // 存在则加入 40 if (room == null) return; 41 42 // 查找房间中是否存在此用户 43 var isExistUser = room.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId); 44 45 // 不存在则加入 46 if (isExistUser == null) 47 { 48 var user = DbContext.Users.Find(u => u.UserId == Context.ConnectionId); 49 user.Rooms.Add(room); 50 room.Users.Add(user); 51 52 // 将客户端的连接ID加入到组里面 53 Groups.Add(Context.ConnectionId, roomName); 54 55 //调用此连接用户的本地JS(显示房间) 56 Clients.Client(Context.ConnectionId).joinRoom(roomName); 57 } 58 else 59 { 60 Clients.Client(Context.ConnectionId).showMessage("请勿重复加入房间!"); 61 } 62 } 63 64 /// <summary> 65 /// 创建聊天室 66 /// </summary> 67 /// <param name="roomName"></param> 68 public void CreateRoom(string roomName) 69 { 70 var room = DbContext.Rooms.Find(a => a.RoomName == roomName); 71 if (room == null) 72 { 73 var cr = new ChatRoom 74 { 75 RoomName = roomName 76 }; 77 78 //将房间加入列表 79 DbContext.Rooms.Add(cr); 80 81 // 本人加入聊天室 82 JoinRoom(roomName); 83 UpdateRoomList(); 84 } 85 else 86 { 87 Clients.Client(Context.ConnectionId).showMessage("房间名重复!"); 88 } 89 } 90 91 public void RemoveUserFromRoom(string roomName) 92 { 93 //查找房间是否存在 94 var room = DbContext.Rooms.Find(a => a.RoomName == roomName); 95 96 //存在则进入删除 97 if (room == null) 98 { 99 Clients.Client(Context.ConnectionId).showMessage("房间名不存在!"); 100 return; 101 } 102 103 // 查找要删除的用户 104 var user = room.Users.FirstOrDefault(a => a.UserId == Context.ConnectionId); 105 // 移除此用户 106 room.Users.Remove(user); 107 //如果房间人数为0,则删除房间 108 if (room.Users.Count <= 0) 109 { 110 DbContext.Rooms.Remove(room); 111 } 112 113 Groups.Remove(Context.ConnectionId, roomName); 114 115 //提示客户端 116 Clients.Client(Context.ConnectionId).removeRoom("退出成功!"); 117 } 118 119 /// <summary> 120 /// 给房间内所有的用户发送消息 121 /// </summary> 122 /// <param name="room">房间名</param> 123 /// <param name="message">信息</param> 124 public void SendMessage(string room, string message) 125 { 126 // 调用房间内所有客户端的sendMessage方法 127 // 因为在加入房间的时候,已经将客户端的ConnectionId添加到Groups对象中了,所有可以根据房间名找到房间内的所有连接Id 128 // 其实我们也可以自己实现Group方法,我们只需要用List记录所有加入房间的ConnectionId 129 // 然后调用Clients.Clients(connectionIdList),参数为我们记录的连接Id数组。 130 Clients.Group(room, new string[0]).sendMessage(room, message + " " + DateTime.Now); 131 }
(3)、在Home控制器里创建GroupUser视图并添加如下代码
1 @{ 2 Layout = null; 3 } 4 5 <!DOCTYPE html> 6 7 <html> 8 <head> 9 <meta name="viewport" content="width=device-width" /> 10 <title>Index</title> 11 <script src="~/Scripts/jquery-1.10.2.min.js"></script> 12 <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script> 13 <script src="~/Scripts/layer/layer.js"></script> 14 <!--这里要注意,这是虚拟目录,也就是你在OWIN Startup中注册的地址--> 15 <script src="/signalr/hubs"></script> 16 17 <script type="text/javascript"> 18 var chat; 19 var roomcount = 0; 20 21 $(function() { 22 chat = $.connection.serverHub; 23 chat.client.showMessage = function(message) { 24 alert(message); 25 }; 26 chat.client.sendMessage = function(roomname, message) { 27 $("#" + roomname).find("ul").each(function() { 28 $(this).append('<li>' + message + '</li>'); 29 }); 30 }; 31 chat.client.removeRoom = function(data) { 32 alert(data); 33 }; 34 chat.client.joinRoom = function (roomname) { 35 var html = '<div style="float:left; margin-left:360px; border:double; height:528px;width:493px" id="' + roomname + '" roomname="' + roomname + '"><button onclick="RemoveRoom(this)">退出</button>\ 36 ' + roomname + '房间\ 37 聊天记录如下:<ul>\ 38 </ul>\ 39 <textarea class="ChatCore_write" id="ChatCore_write" style="width:400px"></textarea> <button onclick="SendMessage(this)">发送</button>\ 40 </div>'; 41 $("#RoomList").append(html); 42 }; 43 44 //注册查询房间列表的方法 45 chat.client.getRoomlist = function(data) { 46 if (data) { 47 var jsondata = $.parseJSON(data); 48 $("#roomlist").html(" "); 49 for (var i = 0; i < jsondata.length; i++) { 50 var html = ' <li>房间名:' + jsondata[i].RoomName + '<button roomname="' + jsondata[i].RoomName + '" onclick="AddRoom(this)">加入</button></li>'; 51 $("#roomlist").append(html); 52 } 53 } 54 }; 55 // 获取用户名称。 56 $('#username').html(prompt('请输入您的名称:', '')); 57 58 $.connection.hub.start().done(function() { 59 $('#CreatRoom').click(function() { 60 chat.server.createRoom($("#Roomname").val()); 61 }); 62 }); 63 }); 64 65 function SendMessage(btn) { 66 var message = $(btn).prev().val(); 67 var room = $(btn).parent(); 68 var username = $("#username").html(); 69 message = username + ":" + message; 70 var roomname = $(room).attr("roomname"); 71 chat.server.sendMessage(roomname, message); 72 $(btn).prev().val('').focus(); 73 } 74 75 function RemoveRoom(btn) { 76 var room = $(btn).parent(); 77 var roomname = $(room).attr("roomname"); 78 chat.server.removeUserFromRoom(roomname); 79 } 80 81 function AddRoom(roomname) { 82 var data =$(roomname).attr("roomname"); 83 chat.server.joinRoom(data); 84 } 85 86 </script> 87 </head> 88 <body> 89 <div> 90 <div>名称:<p id="username"></p></div> 91 输入房间名: 92 <input type="text" value="聊天室1" id="Roomname" /> 93 <button id="CreatRoom">创建聊天室</button> 94 </div> 95 <div style="float:left;border:double"> 96 <div>房间列表</div> 97 <ul id="roomlist"></ul> 98 </div> 99 <div id="RoomList"> 100 </div> 101 </body> 102 </html>
(4)、做好以上步骤之后,主要的功能已经实现,那么接下来我们看下效果 如下
点击发送群聊之后出现如图所示
并创建自己的用户名
我们在创建自己的用户名之后,可以选择自己创建房间或者加入已有的房间,如下图
3、私聊
(1)、私聊我们在这里就不多说了。实现原理和群里差不多。都是找到对应的人员id就ok,直接上图看结果
源码请加qq群:460362190 里面有更多的开源项目哦,欢迎加入讨论
如果你还满意请点击关注和推荐,谢谢