隐藏

SignalR实现消息推送,包括私聊、群聊、在线所有人接收消息(源码)

发布:2020/10/28 16:47:39作者:管理员 来源:本站 浏览次数:865

一、关于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类,(如果创建项目是有这个类。就不用添加了)添加代码如下

1
2
3
4
5
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,直接上图看结果