105

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

 6 years ago
source link: http://www.cnblogs.com/cyzf/p/7808798.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

一、关于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通讯

  

1158526-20171109132723466-1736320060.png

  2、安装SignalR

    (1)、在SignalR通讯 项目下安装 SignalR(找到程序包管理器控制台,输入:Install-Package Microsoft.AspNet.SignalR)

        安装成功后系统会自动生成一个Scripts文件夹,里面存放对应的js文件,如图

  1158526-20171109133520013-143139935.png

  3、安装好环境以后。那么接下来我们就开始搭建我们的环境及编写相应的代码

    (1)、新建一个SignalR集线器ServerHub,如图

  

1158526-20171109134332075-634014396.png

    并编写以下代码

 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)、如果出现如下错误,请删除红框里的代码

    

1158526-20171109141414294-885177941.png

    成功之后的结果,如图

    

1158526-20171109143532747-677478729.png

    你以为完了嘛?还没有,别急往下看

三、页面有优化及私聊、群聊、在线所有人接收消息的实现

  1、发送所有在线人员

    (1)、在layer官网下载layer前端js和ui(在我上一篇的文章中有下载地址,或者直接百度搜索layer),放到你的项目中,在这里我们就不直接说页面代码的编写部分,直接看下我们的效果图,如下

     

1158526-20171109145116528-1161373091.png

    (2)、在完成聊天之前。我们还需要做什么,对做登录,在这里自动生成的用户已经不能满足我们接下来的需求了(登录教程跳过)直接看登录成功过后的界面

    

1158526-20171109145510622-1417916847.png

    

1158526-20171109150248841-1384515407.png

    这样看起来就美观多了,其实在线所有人聊天的功能,就是刚刚我们实现那个功能是一样的,没有任何的变化(只是美观了下),看看聊天结果吧

    

1158526-20171109150406294-959097933.png

      

1158526-20171109150419200-109907045.png

  

1158526-20171109150428481-2141172791.png

      (1)、首先我们的建一个群聊的实体(UserGroup)和房间实体(ChatRoom),如下图

       

1158526-20171109151109606-843592698.png

    (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)、做好以上步骤之后,主要的功能已经实现,那么接下来我们看下效果 如下

    点击发送群聊之后出现如图所示

    

1158526-20171109153212263-1271771435.png

    并创建自己的用户名

    我们在创建自己的用户名之后,可以选择自己创建房间或者加入已有的房间,如下图

    

1158526-20171109153308575-2044225449.png

    

1158526-20171109153318966-221972171.png

    (1)、私聊我们在这里就不多说了。实现原理和群里差不多。都是找到对应的人员id就ok,直接上图看结果

    

1158526-20171109154719559-548862428.png

    

1158526-20171109154731309-600736525.png

源码请加qq群:460362190 里面有更多的开源项目哦,欢迎加入讨论

如果你还满意请点击关注和推荐,谢谢    


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK