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,如图
  
并编写以下代码
public class ServerHub : Hub
{
private static readonly char[] str =
{
'', '', '', '', '', '', '', '', '', '',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z'
}; /// <summary>
/// 消息发送接口
/// </summary>
/// <param name="message"></param>
public void SendMsg(string message)
{
var name = GenerateUserName(); // 调用所有客户端的sendMessage方法
Clients.All.sendMessage(name, message);
} /// <summary>
/// 产生随机用户
/// </summary>
/// <param name="length">用户名长度</param>
/// <returns></returns>
public static string GenerateUserName(int length)
{
var newRandom = new StringBuilder();
var rd = new Random();
for (var i = ; i < length; i++)
{
newRandom.Append(str[rd.Next()]);
}
return newRandom.ToString();
}
(2)、创建一个Startup类,(如果创建项目是有这个类。就不用添加了)添加代码如下
public void Configuration(IAppBuilder app)
{
// 配置集线器
app.MapSignalR();
}
(3)、添加Home控制器及视图Index(此步骤不截图)
(4)、添加Index视图代码 如下
 @{
     Layout = "~/Views/Shared/_Layout.cshtml";
     ViewBag.Title = "聊天窗口";
 }
 <h2>Index</h2>
 <div class="container">
     <input type="text" id="message" />
     <input type="button" id="sendmessage" value="Send" />
     <input type="hidden" id="displayname" />
     <ul id="discussion"></ul>
 </div>
 @section scripts
 {
 <script src="~/Scripts/jquery-1.6.4.min.js"></script>
     <!--引用SignalR库. -->
     <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script>
     <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 -->
     <script src="~/signalr/hubs"></script>
     <script>
         $(function () {
             // 引用集线器代理
             var chat = $.connection.serverHub;
             // 定义服务器端调用的客户端sendMessage来显示新消息
             chat.client.sendMessage = function (name, message) {
                 // 向页面添加消息
                 $('#discussion').append('<li><strong>' + htmlEncode(name)
                     + '</strong>: ' + htmlEncode(message) + '</li>');
             };
             // 设置焦点到输入框
             $('#message').focus();
             // 开始连接服务器
             $.connection.hub.start().done(function () {
                 $('#sendmessage').click(function () {
                     // 调用服务器端集线器的SendMsg方法
                     chat.server.sendMsg($('#message').val());
                     // 清空输入框信息并获取焦点
                     $('#message').val('').focus();
                 });
             });
         });
         // 为显示的消息进行Html编码
         function htmlEncode(value) {
             var encodedValue = $('<div />').text(value).html();
             return encodedValue;
         }
     </script>
 }
4、完成以上步骤之后,我们的SignalR算是简单的完成。让我们来看下制作结果吧
(1)、如果出现如下错误,请删除红框里的代码
    
成功之后的结果,如图
    
你以为完了嘛?还没有,别急往下看
三、页面有优化及私聊、群聊、在线所有人接收消息的实现
1、发送所有在线人员
(1)、在layer官网下载layer前端js和ui(在我上一篇的文章中有下载地址,或者直接百度搜索layer),放到你的项目中,在这里我们就不直接说页面代码的编写部分,直接看下我们的效果图,如下
     
(2)、在完成聊天之前。我们还需要做什么,对做登录,在这里自动生成的用户已经不能满足我们接下来的需求了(登录教程跳过)直接看登录成功过后的界面
    
    
这样看起来就美观多了,其实在线所有人聊天的功能,就是刚刚我们实现那个功能是一样的,没有任何的变化(只是美观了下),看看聊天结果吧
    
      
  
2、群聊
(1)、首先我们的建一个群聊的实体(UserGroup)和房间实体(ChatRoom),如下图
      
(2)、在我们建好的ServerHub类里编写如下代码(具体实现看源码)
 public static ChatContext DbContext = new ChatContext();
         // 重写Hub连接断开的事件  (断线时调用)
         public override Task OnDisconnected(bool stopCalled)
         {
             // 查询用户
             var user = DbContext.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId);
             if (user != null)
             {
                 // 删除用户
                 DbContext.Users.Remove(user);
                 // 从房间中移除用户
                 foreach (var item in user.Rooms)
                 {
                     RemoveUserFromRoom(item.RoomName);
                 }
             }
             return base.OnDisconnected(stopCalled);
         }
         // 为所有用户更新房间列表
         public void UpdateRoomList()
         {
             var itme = DbContext.Rooms.Select(p => new { p.RoomName });
             var jsondata = JsonHelper.ToJsonString(itme.ToList());
             Clients.All.getRoomlist(jsondata);
         }
         /// <summary>
         /// 加入聊天室
         /// </summary>
         public void JoinRoom(string roomName)
         {
             // 查询聊天室
             var room = DbContext.Rooms.Find(p => p.RoomName == roomName);
             // 存在则加入
             if (room == null) return;
             // 查找房间中是否存在此用户
             var isExistUser = room.Users.FirstOrDefault(u => u.UserId == Context.ConnectionId);
             // 不存在则加入
             if (isExistUser == null)
             {
                 var user = DbContext.Users.Find(u => u.UserId == Context.ConnectionId);
                 user.Rooms.Add(room);
                 room.Users.Add(user);
                 // 将客户端的连接ID加入到组里面
                 Groups.Add(Context.ConnectionId, roomName);
                 //调用此连接用户的本地JS(显示房间)
                 Clients.Client(Context.ConnectionId).joinRoom(roomName);
             }
             else
             {
                 Clients.Client(Context.ConnectionId).showMessage("请勿重复加入房间!");
             }
         }
         /// <summary>
         /// 创建聊天室
         /// </summary>
         /// <param name="roomName"></param>
         public void CreateRoom(string roomName)
         {
             var room = DbContext.Rooms.Find(a => a.RoomName == roomName);
             if (room == null)
             {
                 var cr = new ChatRoom
                 {
                     RoomName = roomName
                 };
                 //将房间加入列表
                 DbContext.Rooms.Add(cr);
                 // 本人加入聊天室
                 JoinRoom(roomName);
                 UpdateRoomList();
             }
             else
             {
                 Clients.Client(Context.ConnectionId).showMessage("房间名重复!");
             }
         }
         public void RemoveUserFromRoom(string roomName)
         {
             //查找房间是否存在
             var room = DbContext.Rooms.Find(a => a.RoomName == roomName);
             //存在则进入删除
             if (room == null)
             {
                 Clients.Client(Context.ConnectionId).showMessage("房间名不存在!");
                 return;
             }
             // 查找要删除的用户
             var user = room.Users.FirstOrDefault(a => a.UserId == Context.ConnectionId);
             // 移除此用户
             room.Users.Remove(user);
             //如果房间人数为0,则删除房间
             if (room.Users.Count <= )
             {
                 DbContext.Rooms.Remove(room);
             }
             Groups.Remove(Context.ConnectionId, roomName);
             //提示客户端
             Clients.Client(Context.ConnectionId).removeRoom("退出成功!");
         }
         /// <summary>
         /// 给房间内所有的用户发送消息
         /// </summary>
         /// <param name="room">房间名</param>
         /// <param name="message">信息</param>
         public void SendMessage(string room, string message)
         {
             // 调用房间内所有客户端的sendMessage方法
             // 因为在加入房间的时候,已经将客户端的ConnectionId添加到Groups对象中了,所有可以根据房间名找到房间内的所有连接Id
             // 其实我们也可以自己实现Group方法,我们只需要用List记录所有加入房间的ConnectionId
             // 然后调用Clients.Clients(connectionIdList),参数为我们记录的连接Id数组。
             Clients.Group(room, new string[]).sendMessage(room, message + " " + DateTime.Now);
         }
(3)、在Home控制器里创建GroupUser视图并添加如下代码
 @{
     Layout = null;
 }
 <!DOCTYPE html>
 <html>
 <head>
     <meta name="viewport" content="width=device-width" />
     <title>Index</title>
     <script src="~/Scripts/jquery-1.10.2.min.js"></script>
     <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script>
     <script src="~/Scripts/layer/layer.js"></script>
     <!--这里要注意,这是虚拟目录,也就是你在OWIN Startup中注册的地址-->
     <script src="/signalr/hubs"></script>
     <script type="text/javascript">
         var chat;
         var roomcount = ;
         $(function() {
             chat = $.connection.serverHub;
             chat.client.showMessage = function(message) {
                 alert(message);
             };
             chat.client.sendMessage = function(roomname, message) {
                 $("#" + roomname).find("ul").each(function() {
                     $(this).append('<li>' + message + '</li>');
                 });
             };
             chat.client.removeRoom = function(data) {
                 alert(data);
             };
             chat.client.joinRoom = function (roomname) {
                 var html = '<div style="float:left; margin-left:360px; border:double; height:528px;width:493px" id="' + roomname + '" roomname="' + roomname + '"><button onclick="RemoveRoom(this)">退出</button>\
                                     ' + roomname + '房间\
                                                 聊天记录如下:<ul>\
                                                 </ul>\
                                     <textarea class="ChatCore_write" id="ChatCore_write" style="width:400px"></textarea> <button onclick="SendMessage(this)">发送</button>\
                                     </div>';
                 $("#RoomList").append(html);
             };
             //注册查询房间列表的方法
             chat.client.getRoomlist = function(data) {
                 if (data) {
                     var jsondata = $.parseJSON(data);
                     $("#roomlist").html(" ");
                     for (var i = ; i < jsondata.length; i++) {
                         var html = ' <li>房间名:' + jsondata[i].RoomName + '<button roomname="' + jsondata[i].RoomName + '" onclick="AddRoom(this)">加入</button></li>';
                         $("#roomlist").append(html);
                     }
                 }
             };
             // 获取用户名称。
             $('#username').html(prompt('请输入您的名称:', ''));
             $.connection.hub.start().done(function() {
                 $('#CreatRoom').click(function() {
                     chat.server.createRoom($("#Roomname").val());
                 });
             });
         });
         function SendMessage(btn) {
             var message = $(btn).prev().val();
             var room = $(btn).parent();
             var username = $("#username").html();
             message = username + ":" + message;
             var roomname = $(room).attr("roomname");
             chat.server.sendMessage(roomname, message);
             $(btn).prev().val('').focus();
         }
         function RemoveRoom(btn) {
             var room = $(btn).parent();
             var roomname = $(room).attr("roomname");
             chat.server.removeUserFromRoom(roomname);
         }
         function AddRoom(roomname) {
             var data =$(roomname).attr("roomname");
             chat.server.joinRoom(data);
         }
     </script>
 </head>
 <body>
     <div>
         <div>名称:<p id="username"></p></div>
         输入房间名:
         <input type="text" value="聊天室1" id="Roomname" />
         <button id="CreatRoom">创建聊天室</button>
     </div>
     <div style="float:left;border:double">
         <div>房间列表</div>
         <ul id="roomlist"></ul>
     </div>
     <div id="RoomList">
     </div>
 </body>
 </html>
(4)、做好以上步骤之后,主要的功能已经实现,那么接下来我们看下效果 如下
点击发送群聊之后出现如图所示
    
并创建自己的用户名
我们在创建自己的用户名之后,可以选择自己创建房间或者加入已有的房间,如下图
    
    
3、私聊
(1)、私聊我们在这里就不多说了。实现原理和群里差不多。都是找到对应的人员id就ok,直接上图看结果
    
    
源码请加qq群:460362190 里面有更多的开源项目哦,欢迎加入讨论
如果你还满意请点击关注和推荐,谢谢
SignalR实现消息推送,包括私聊、群聊、在线所有人接收消息(源码)的更多相关文章
- Android P正式版即将到来:后台应用保活、消息推送的真正噩梦
		1.前言 对于广大Android开发者来说,Android O(即Android 8.0)还没玩热,Andriod P(即Andriod 9.0)又要来了. 下图上谷歌官方公布的Android P ... 
- 友盟消息推送api、python sdk问题、测试demo代码
		一,友盟消息推送python服务端sdk地址和文档地址 1.sdk地址:http://dev.umeng.com/system/resources/W1siZiIsIjIwMTYvMDgvMTkvMT ... 
- 实现websocket 主动消息推送,用laravel+Swoole
		近来有个需求:想实现一个可以主动触发消息推送的功能,这个可以实现向模板消息那个,给予所有成员发送自定义消息,而不需要通过客户端发送消息,服务端上message中监听传送的消息进行做相对于的业务逻辑. ... 
- Android 基于Netty的消息推送方案之Hello World(一)
		消息推送方案(轮询.长连接) 轮询 轮询:比较简单的,最容易理解和实现的就是客户端去服务器上拉信息,信息的及时性要求越高则拉信息的频率越高.客户端拉信息的触发可以是一些事件,也可以是一个定时器,不断地 ... 
- 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)
		1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ... 
- 了解iOS消息推送一文就够:史上最全iOS Push技术详解
		本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ... 
- How Javascript works (Javascript工作原理) (九) 网页消息推送通知机制
		个人总结: 1.介绍了网页消息推送通知机制 全文地址:https://github.com/Troland/how-javascript-works 这是 JavaScript 工作原理的第九章. 现 ... 
- 我有 7种 实现web实时消息推送的方案,7种!
		技术交流,公众号:程序员小富 大家好,我是小富- 我有一个朋友- 做了一个小破站,现在要实现一个站内信web消息推送的功能,对,就是下图这个小红点,一个很常用的功能. 不过他还没想好用什么方式做,这里 ... 
- iOS 10 消息推送(UserNotifications)秘籍总结(一)
		前言 之前说会单独整理消息通知的内容,但是因为工(就)作(是)的(很)事(懒)没有更新文章,违背了自己的学习的初衷.因为互联网一定要有危机意识,说不定眼一睁,我们就out丢了饭碗. 图片来源网络.jp ... 
随机推荐
- mysql用户权限配置
			创建管理员: mysqladmin -u root password 123456 登录 mysql -u root -p 建库及授权 > create database bdp charact ... 
- R语言画曲线图
			本文以1950年到2010年期间我国的火灾统计数据为例,数据如下所示: (0)加载数据 data<-read.csv("E:\\MyDocument\\p\\Data\\1950~20 ... 
- Nunit测试工具使用实例
			前言: 本文主要是介绍了Nunit的基本使用,其中参详了很多已有的文章,由于最近要使用其进行测试,所以对网上的文章做了下整理,同时加入了一些自己的实践. NUnit的属性 TestFixture 它标 ... 
- SQL Server XML数据解析
			--5.读取XML --下面为多种方法从XML中读取EMAIL DECLARE @x XML SELECT @x = ' <People> <dongsheng> <In ... 
- cocos2dx - 控件扩展之pageview循环显示
			接上一节内容:cocos2dx - shader实现任意动画的残影效果 本节主要讲一下扩展PageView控件功能 在实际游戏应用中,经常会碰到用原来的控件难以实现的功能.这时候就需要根据需求,通过选 ... 
- Github Page--CSDN新人的第二选择
			我也是个CSDN新人,使用的CSDN的初衷应该和众人类似,就是想总结下平时的学习成果,或者一些想法. CSDN好的地方: 书写界面简洁,支持markdown语法 人还算多,也比较年轻 相对较活跃 内容 ... 
- wpf C# 数据库 c/s 个人信息管理 wpf局域网通信
			系统功能基本要求 wpf局域网通信 WPF跨线程访问线程安全的数据如解决该类型的CollectionView不支持从调度程序线程以外的线程对其SourceCollection 读取信息null 读取发 ... 
- JAVA提高六:泛型
			在面向对象编程语言中,多态算是一种泛化机制.例如,你可以将方法的参数类型设置为基类,那么该方法就可以接受从这个基类中导出的任何类作为参数,这样的方法将会更具有通用性.此外,如果将方法参数声明为接口,将 ... 
- 自学 Python 3 最好的 入门 书籍 推荐(附 免费 在线阅读 下载链接)
			请大家根据自己的实际情况对号入座,挑选适合自己的 Python 入门书籍: 完全没有任何编程基础:01 号书 少量编程基础,不求全,只希望能以最快的速度入门:02 号书 少量编程基础,有一定的英文阅读 ... 
- ThreadPoolExecutor系列<二、ThreadPoolExecutor 代码流程图>
			本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7681648.html 1.ThreadPoolExecutor代码 ... 
