Asp.net MVC企业级开发(04)---SignalR消息推送
Asp.net SignalR是微软为实现实时通信而开发的一个类库。可以适用于以下场景:
- 聊天室,如在线客服系统,IM系统等
- 股票价格实时更新
- 消息的推送服务
- 游戏中人物位置的实时推送
SignalR可以进行远程分布式实时通信,都是使用远程代理来实现,其中有两大内部对象,第一个是Persisten Connection,用于客户端和服务器端的持久连接,第二个是Hub(集线器)对象,主要用于信息交互,将服务器端的数据推送(push)至客户端,大致原理如下:
- 客户端建立与服务器端的连接
- 客户端调用服务器端的方法
- 服务器端通过客户端发送的请求,响应数据,调用客户端的方法将数据推送至客户端
4.1 SignalR的基本使用
接下来我们通过消息实时推送的案例来学习 SignalR 的使用步骤。具体操作步骤如下:
- 创建一个应用程序,我这里创建的是MVC应用程序
- 在MVC项目的Models文件夹中添加新项 SignalR集线器类。如图所示。
创建完成之后,在应用程序的Scripts文件夹里面会自动生成两个js文件,如图所示:
在创建的Hub类中添加如下代码:
//hub别名,方便前台调用 [HubName("getMsg")] public class MyHub : Hub { public void Send(string title,string msg) { //调用客户端的sendMessage()方法 Clients.All.sendMessage(title,msg); } } |
其中Clients.All是dynamic类型,sendMessaage()方法是视图中js定义的function。HubName特性用来给集线器类定义别名。
- 在项目中添加 OWIN StartUp 类。如图所示。
代码如下:
public class Startup { public void Configuration(IAppBuilder app) { //注册管道,使用默认的虚拟地址,根目录下的"/signalr", //当然你也可以自己定义 app.MapSignalR(); } } |
- 创建控制器和视图
创建MessageController,并创建两个用于显示视图的action。
控制器代码:
public class MessageController : Controller { //发送消息 public ActionResult SendMessage() { return View(); } //接收消息 public ActionResult ReceiveMessage() { return View(); } } |
发送消息视图代码:
<h2>发送消息</h2> <div> 标题:<input type="text" id="title" /> </div><br /> <div> 内容:<textarea id="message" rows="4" cols="30"></textarea> </div> <br /> <div> <input type="button" id="sendmessage" value="发送" /> </div> <script src="~/Scripts/jquery.signalR-2.2.2.js"></script> <!--引用自动生成的SignalR 集线器(Hub)脚本.在运行的时候在浏览器的Source下可看到 --> <script src="~/signalr/hubs"></script> <script type="text/javascript"> $(function () { // 引用自动生成的集线器代理(此处使用别名getMsg) var chat = $.connection.getMsg; // 集成器连接开始 $.connection.hub.start().done(function () { // 服务连接完成,给发送按钮注册单击事件 $('#sendmessage').click(function () { // 调用服务器端集线器的Send方法 chat.server.send($("#title").val(), $('#message').val()); }); }); }); </script> |
接收消息视图代码:
<h2>接收消息</h2> <div> <br /> <div id="msgcontent"></div> </div> <script src="~/Scripts/jquery.signalR-2.2.2.js"></script> <script src="~/signalr/hubs"></script> <script type="text/javascript"> $(function () { // 引用自动生成的集线器代理 var chat = $.connection.getMsg; // 定义服务器端调用的客户端sendMessage来显示新消息 chat.client.sendMessage = function (title, message) { // 向页面发送接收的消息 var html = "<div>标题:" + title + "消息内容:" + message + "</div>"; $("#msgcontent").after(html); }; // 集成器连接开始 $.connection.hub.start(); }); </script> |
运行程序的时候,页面就与SignalR的服务建立了连接,具体的建立连接的代码就是:$.connection.hub.start()。这句代码的作用就是与SignalR服务建立连接,后面的done函数表明建立连接成功后为发送按钮注册了一个click事件,当客户端输入内容点击发送按钮后,该Click事件将会触发,触发执行的操作为: chat.server.send($("#title").val(), $('#message').val());。这句代码表示调用服务端的send函数,而服务端的Send方法又调用所有客户端的sendMessage函数,而客户端中sendMessage函数就是将信息添加到对应的消息列表中。这样就实现了广播消息的功能了。
在服务端声明的所有Hub信息,都会生成JavaScript输出到客户端,为了验证这一点,可以在Chrome中F12来查看源码就明白了,具体如下图所示:
看到上图,也就明白了为什么页面需要引入"signalr/hubs"脚本库了。
<!--引用SignalR库. --> <script src="~/Scripts/jquery.signalR-2.2.0.min.js"></script> <!--引用自动生成的SignalR 集线器(Hub)脚本 --> <script src="~/signalr/hubs"></script> |
4.2 使用SignalR实现点对点聊天
介绍了群发消息的实现,下面来学习如何实现像QQ一样的点对点聊天。
Clients.All.sendMessage(title, message)表示调用所有客户端的SendMessage方法。除了All属性外,还具有其他属性,可以在VS中按F12来查看Clients对象的所有属性或方法,具体的定义如下:
public interface IHubConnectionContext<T> { T All { get; } // 代表所有客户端 T AllExcept(params string[] excludeConnectionIds); //除了参数中的所有客户端 T Client(string connectionId); // 特定的客户端,实现端对端聊天的关键 T Clients(IList<string> connectionIds); // 参数中的客户端 T Group(string groupName, params string[] excludeConnectionIds); // 指定客户端组,可以实现群聊 T Groups(IList<string> groupNames, params string[] excludeConnectionIds); T User(string userId); // 特定的用户 T Users(IList<string> userIds); // 参数中的用户 } |
SignalR会每一个客户端分配一个ConnnectionId,这样我们就可以通过ConnnectionId来找到特定的客户端了。我们在向某个客户端发送消息的时候,除了要将消息传入,也需要将发送给对方的ConnectionId输入,这样服务端就能根据传入的ConnectionId来转发对应的消息给对应的客户端了。这样也就完成了端对端聊天的功能。另外,如果用户如果不在线的话,服务端可以把消息保存到数据库中,等对应的客户端上线的时候,再从数据库中查看该客户端是否有消息需要推送,有的话,从数据库取出数据,将该数据推送给该客户端。
下面我们来梳理下端对端聊天功能的实现思路:
- 客户端登入的时候记录下客户端的ConnnectionId,并将用户加入到一个静态数组中,该数据为了记录所有在线用户。
- 用户可以点击在线用户中的用户聊天,在发送消息的时候,需要将ConnectionId一并传入到服务端。
- 服务端根据传入的消息内容和ConnectionId调用Clients.Client(connnection).sendMessage方法来进行转发到对应的客户端。
根据上面的思路,先来实现集线器中的代码:
[HubName("Chat")] public class ChatHub : Hub { // 静态属性,在线用户列表 public static List<UserInfo> OnlineUsers = new List<UserInfo>(); //登录 public void Login(string userId,string userName) { //获取客户端的ConnectionId var connnectId = Context.ConnectionId; OnlineUsers.Add(new UserInfo { ConnectionId = connnectId, UserId = userId, UserName = userName }); // 所有客户端同步在线用户 Clients.All.loadUser(OnlineUsers); } /// <summary> /// 发送私聊 /// </summary> /// <param name="toUserId">接收方用户连接ID</param> /// <param name="message">内容</param> public void SendPrivateMessage(string toUserId, string message) { var fromUserId = Context.ConnectionId; var toUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == toUserId); var fromUser = OnlineUsers.FirstOrDefault(x => x.ConnectionId == fromUserId); if (toUser != null && fromUser != null) { // 调用指定用户的客户端方法 Clients.Client(toUserId).receivePrivateMessage(fromUser.UserName, message); } else { //表示对方不在线 Clients.Caller.absentSubscriber(); } } /// <summary> /// 断线时调用 /// </summary> /// <param name="stopCalled"></param> /// <returns></returns> public override Task OnDisconnected(bool stopCalled) { var user = OnlineUsers.FirstOrDefault(u => u.ConnectionId == Context.ConnectionId); // 判断用户是否存在,存在则删除 if (user == null) return base.OnDisconnected(stopCalled); Clients.All.onUserDisconnected(user.ConnectionId, user.UserName); //调用客户端用户离线通知 // 删除用户 OnlineUsers.Remove(user); return base.OnDisconnected(stopCalled); } } |
客户端代码:
<h2>聊天系统</h2> <div class="container"> @using (Html.BeginForm("login", "Chat", FormMethod.Post, new { @class = "form-inline" })) { <label>用户Id:</label> @Html.TextBox("userId", "", new { @class = "form-control" }) <label>用户名:</label> @Html.TextBox("userName", "", new { @class = "form-control" }) <input type="button" id="btnLogin" value="登录" class="btn btn-default" /> } </div> <hr /> <div class="container"> <div class="col-md-3"> <div class="panel panel-default"> <div class="panel-heading"> 在线用户 </div> <div class="panel-body"> <ul id="userList"> </ul> </div> </div> </div> <div class="col-md-8"> <div class="panel panel-default"> <div class="panel-heading"> 聊天内容 </div> <div class="panel-body"> <ul id="msgList" > </ul> </div> <div class="panel-footer"> <label>消息To:</label> <label id="toUser"></label> @Html.TextBox("msg", "", new { @class = "form-control form-inline" }) <input type="button" id="btnSend" value="发送" class="btn btn-default" /> </div> </div> </div> </div> @section scripts{ <script src="~/Scripts/jquery.signalR-2.2.2.min.js"></script> <script src="~/signalr/hubs"></script> <script> //用户点击(选择用户) function selectUser(li) { var connectionId = $(li).attr("cid"); $('#toUser').text(connectionId); } $(function () { var chat = $.connection.Chat; //刷新在线列表 chat.client.loadUser = function (allUsers) { $('#userList').html(""); for (var i = 0; i < allUsers.length; i++) { var li = $('<li cid="' + allUsers[i].ConnectionId+'" onclick="selectUser(this)">' + allUsers[i].UserId + ':' + allUsers[i].UserName + '</li> '); $('#userList').append(li); } } //接收消息 chat.client.receivePrivateMessage = function (from, msg) { var li = $("<li>来自:" + from + "<br/>" + msg + "</li>"); $("#msgList").append(li); } $.connection.hub.start().done(function () { console.log("连接完成"); //登录 $('#btnLogin').click(function () { chat.server.login($("#userId").val(), $('#userName').val()) }) $('#btnSend').click(function () { chat.server.sendPrivateMessage($('#toUser').text(), $('#msg').val()) }) }); }) </script> |
Asp.net MVC企业级开发(04)---SignalR消息推送的更多相关文章
- asp.net mvc 实现简单的实时消息推送
因为项目需要,需要在网页上实现消息的推送.在百度上搜索了一下,发现实现网页上的消息推送,可以使用asp.net 中的SignalR类库,当然也可以使用H5的WebSocket Ajax的轮回.当然此 ...
- WinForm中 Asp.Net Signalr消息推送测试实例
p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...
- iOS开发如何实现消息推送机制
一.关于推送通知 推送通知,也被叫做远程通知,是在iOS 3.0以后被引入的功能.是当程序没有启动或不在前台运行时,告诉用户有新消息的一种途径,是从外部服务器发送到应用程序上的.一般说来,当要显示消息 ...
- java开发微信模板消息推送
发布时间:2018-12-12 技术:springboot+maven 概述 该demo主要涉及微信模板消息推送功能, 详细 代码下载:http://www.demodashi.com/dem ...
- 微信小程序开发:设置消息推送
开发设置中,启用并设置消息推送配置后,用户发给小程序的消息以及开发者需要的事件推送,都将被微信转发至该服务器地址中. 不过照着说明去操作,即使按照最简单的明文方式去设置,还是提示Token验证失败.仔 ...
- Asp.net MVC企业级开发(01)---Autofac
1.1 控制反转 在面向对象设计的软件系统中,它的底层都是由N个对象构成的,各个对象之间通过相互合作,最终实现系统的业务逻辑.同时,对象之间的耦合关系是无法避免的,也是必要的,这是协同工作的基础.但是 ...
- signalR 消息推送
业务情景一:上传报表,上传excel.如果excel的数据量很大,上万条,上十万条数据,那么这个上传请求必然是个耗时请求.用户上传之后,很关心上传的进度和结果. 业务情景二:站内消息提醒,实时有效地接 ...
- asp.net web 通过IHttpAsyncHandler接口进行消息推送
.消息类,可直接通过这个类推送消息 HttpMessages using System; using System.Collections.Generic; using System.Linq; us ...
- Asp.net MVC企业级开发(02)---Log4net
Log4Net 是用来记录日志的,可以将程序运行过程中的信息输出到一些地方(文件.数据库.EventLog等).日志就是程序的“黑匣子”,可以通过日志查看系统的运行过程,从而发现系统的问题. 日志的作 ...
随机推荐
- 5.如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
作者:中华石杉 面试题 如何基于 dubbo 进行服务治理.服务降级.失败重试以及超时重试? 面试官心理分析 服务治理,这个问题如果问你,其实就是看看你有没有服务治理的思想,因为这个是做过复杂微服务的 ...
- Docker 网络简单说明
docker0 网络模型小结 Docker Daemon 会创建出一个名为 docker0 的虚拟网桥 ,用来连接宿主机与容器,或者连接不同的容器. veth pair 是用于不同network na ...
- NLP中的预训练语言模型(二)—— Facebook的SpanBERT和RoBERTa
本篇带来Facebook的提出的两个预训练模型——SpanBERT和RoBERTa. 一,SpanBERT 论文:SpanBERT: Improving Pre-training by Represe ...
- 201871010109-胡欢欢《面向对象程序设计(java)》第6-7周学习总结
实验六 继承定义与使用 实验时间 2019-9-29 第一部分:理论部分. 1.继承:已有类来构建新类的一种机制.档定义了一个新类继承另一个类时,这个新类就继承了这个类的方法和域,同时在新类中添加新的 ...
- 常用开窗函数总结(hive、sparkSQL可执行)
一:根据某个字段排序 测试数据: SQL> select * from sscore; NAME SCORE ---------- ----- aa 99 bb ...
- HTTP几种认证方式介绍
HTTP提供了一套标准的身份验证框架:服务器可以用来针对客户端的请求发送质询(challenge),客户端根据质询提供身份验证凭证.质询与应答的工作流程如下:服务器端向客户端返回401(Unautho ...
- ios、安卓的兼容性
日期转换成时间戳: 安卓下可以使用 Date.parse(new Date('2019-11-18 12:00:00')) 直接转换,结果为 1574049600000 ios下 Date.parse ...
- Codeforces Round #507 (Div. 2, based on Olympiad of Metropolises) D mt19937
https://codeforces.com/contest/1040/problem/D 用法 mt19937 g(种子); //种子:time(0) mt19937_64 g(); //long ...
- css3中@font-face模块自定义字体
一.@font-face模块介绍 @font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,在Web的开发中使用字体不再只能使用W ...
- Linux性能优化实战学习笔记:第二十一讲
一 内存性能指标 1.系统内存使用情况 共享内存:是通过tmpfs实现的,所以它的大小也就是tmpfs使用的大小了tmpfs其实也是一种特殊的缓存 可用内存:是新进程可以使用的最大内存它包括剩余内存和 ...