博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过三个DEMO学会SignalR的三种实现方式
阅读量:6208 次
发布时间:2019-06-21

本文共 7891 字,大约阅读时间需要 26 分钟。

原文:

一、理解SignalR

ASP .NET SignalR 是一个ASP .NET 下的类库,可以在ASP .NET 的Web项目中实现实时通信(即:客户端(Web页面)和服务器端可以互相实时的通知消息及调用方法),SignalR有三种传输模式:LongLooping(长轮询)、WebSocket(HTML5的WEB套接字)、Forever Frame(隐藏框架的长请求连接),可以在WEB客户端显式指定一种或几种,也可以采取默认(推荐),若采取默认,SignalR会根据浏览器的环境自动选择合适的传输方式。

二、SignalR的三种实现方式

第一种:采用集线器类(Hub)+非自动生成代理模式:服务端与客户端分别定义的相对应的方法,客户端通过代理对象调用服务端的方法,服务端通过IHubConnectionContext回调客户端的方法,客户端通过回调方法接收结果。

之前我写过一篇文章《》,是通过长轮询+长连接的方式来实现的在线多人聊天室功能,从代码量来看就知道实现起来并不简单,而如今有了SignalR,会简单很多,我这里使用SignalR再来写一个简单的在线多人聊天室示例,以便大家快速掌握SignalR。

DEMO - 1 示例代码如下:

服务端:

//Startup类文件using System;using System.Threading.Tasks;using Microsoft.Owin;using Owin;using Microsoft.AspNet.SignalR;[assembly: OwinStartup(typeof(TestWebApp.Models.Startup))]namespace TestWebApp.Models{    public class Startup    {        public void Configuration(IAppBuilder app)        {            app.MapSignalR();        }    }}//ChatHub类文件using Microsoft.AspNet.SignalR;using Microsoft.AspNet.SignalR.Hubs;using System;using System.Collections.Concurrent;using System.Collections.Generic;using System.Linq;using System.Web;namespace TestWebApp.Models{    [HubName("chat")]    public class ChatHub : Hub    {        public static ConcurrentDictionary
OnLineUsers = new ConcurrentDictionary
(); [HubMethodName("send")] public void Send(string message) { string clientName = OnLineUsers[Context.ConnectionId]; message = HttpUtility.HtmlEncode(message).Replace("\r\n", "
").Replace("\n", "
"); Clients.All.receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), clientName, message); } [HubMethodName("sendOne")] public void Send(string toUserId, string message) { string clientName = OnLineUsers[Context.ConnectionId]; message = HttpUtility.HtmlEncode(message).Replace("\r\n", "
").Replace("\n", "
"); Clients.Caller.receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("您对 {1}", clientName, OnLineUsers[toUserId]), message); Clients.Client(toUserId).receiveMessage(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 对您", clientName), message); } public override System.Threading.Tasks.Task OnConnected() { string clientName = Context.QueryString["clientName"].ToString(); OnLineUsers.AddOrUpdate(Context.ConnectionId, clientName, (key, value) => clientName); Clients.All.userChange(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 加入了。", clientName), OnLineUsers.ToArray()); return base.OnConnected(); } public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled) { string clientName = Context.QueryString["clientName"].ToString(); Clients.All.userChange(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), string.Format("{0} 离开了。", clientName), OnLineUsers.ToArray()); OnLineUsers.TryRemove(Context.ConnectionId, out clientName); return base.OnDisconnected(stopCalled); } }}

 

public ActionResult Index()        {            ViewBag.ClientName = "聊客-" + Guid.NewGuid().ToString("N");            var onLineUserList = ChatHub.OnLineUsers.Select(u => new SelectListItem() { Text = u.Value, Value = u.Key }).ToList();            onLineUserList.Insert(0, new SelectListItem() { Text = "-所有人-", Value = "" });            ViewBag.OnLineUsers = onLineUserList;            return View();        }

 

WEB客户端:

    
聊天室

大众聊天室

聊天名称: @Html.TextBox("clientname", ViewBag.ClientName as string, new { @readonly = "readonly", style = "width:300px;" })
聊天对象: @Html.DropDownList("users", ViewBag.OnLineUsers as IEnumerable
)
@Html.TextArea("message", new { rows = 5, style = "width:500px;" })

服务端与客户端代码都比较简单,网上相关的说明也有,这里就不再解说了,只说一下这种方式JS端调用服务端方法采用:chat.invoke,而被服务端回调的方法则采用:chat.on (这里的chat是createHubProxy创建得来的)

第二种:采用集线器类(Hub)+自动生成代理模式

DEMO - 2 示例代码如下:

服务端与DEMO 1相同,无需改变

客户端:

    
聊天室

大众聊天室

聊天名称: @Html.TextBox("clientname", ViewBag.ClientName as string, new { @readonly = "readonly", style = "width:300px;" })
聊天对象: @Html.DropDownList("users", ViewBag.OnLineUsers as IEnumerable
)
@Html.TextArea("message", new { rows = 5, style = "width:500px;" })

上述代码中特别需要注意的是,需要引用一个“不存在的JS目录”:<script src="~/signalr/hubs" type="text/javascript"></script>,为什么要打引号,是因为我们在写代码的时候是不存在的,而当运行后就会自动生成signalr的代理脚本,这就是与非自动生成代理脚本最根本的区别,也正是因为这个自动生成的脚本,我们可以在JS中更加方便的调用服务端方法及定义回调方法,调用服务端方法采用:chat.server.XXX,而被服务端回调的客户端方法则采用:chat.client.XXX

看一下上述两种的运行效果截图吧:

 

第三种:采用持久化连接类(PersistentConnection)

 DEMO - 3 示例代码如下:

服务端:

//Startup类:using System;using System.Threading.Tasks;using Microsoft.Owin;using Owin;using Microsoft.AspNet.SignalR;[assembly: OwinStartup(typeof(TestWebApp.Models.Startup))]namespace TestWebApp.Models{    public class Startup    {        public void Configuration(IAppBuilder app)        {            app.MapSignalR
("/MyConnection"); } }}//MyConnection类:using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;using System.Web;using Microsoft.AspNet.SignalR;namespace TestWebApp.Models{ public class MyConnection : PersistentConnection { private static List
monitoringIdList = new List
(); protected override Task OnConnected(IRequest request, string connectionId) { bool IsMonitoring = (request.QueryString["Monitoring"] ?? "").ToString() == "Y"; if (IsMonitoring) { if (!monitoringIdList.Contains(connectionId)) { monitoringIdList.Add(connectionId); } return Connection.Send(connectionId, "ready"); } else { if (monitoringIdList.Count > 0) { return Connection.Send(monitoringIdList, "in_" + connectionId); } else { return Connection.Send(connectionId, "nobody"); } } } protected override Task OnReceived(IRequest request, string connectionId, string data) { if (monitoringIdList.Contains(connectionId)) { return Connection.Send(data, "pass"); } return null; } protected override Task OnDisconnected(IRequest request, string connectionId, bool stopCalled) { if (!monitoringIdList.Contains(connectionId)) { return Connection.Send(monitoringIdList, "out_" + connectionId); } return null; } }}

WEB客户端:

MonitoringPage
以下是实时监控到进入EnterPage页面的用户情况:(服务状况:
)
用户进入消息 授 权
EnterPage
该页面浏览受限,已自动将您的浏览请求发给管理员,请稍候。。。

上述代码可以看出与采用Hub(集线器类)的不同之处,一是:Startup.Configuration中是需要指定app.MapSignalR<MyConnection>("/MyConnection"),二是需实现继承自PersistentConnection类的自定义的持久化连接类,在这个连接中可以重写:OnConnected、OnDisconnected、OnReceived、OnReconnected、ProcessRequest方法,同时有几个重要的属性成员Connection、Groups,服务端发消息给客户端采用:Connection.Broadcast(广播,所有客户端都可以收到消息),Connection.Send(发送给指定的客户端)

运行效果如下截图示:

 

 

 

 

SignalR支持额外附加:QueryString、Cookie、State,具体的客户端设置与服务端接收请见上面的代码,同时也可以参见如下其它博主总结的表格():

转载地址:http://nbzja.baihongyu.com/

你可能感兴趣的文章
CentOS 中使用yum出现的“UnicodeDecodeError: &#39;ascii&#39; codec”问题解决方法
查看>>
【知识整理】这可能是最好的RxJava 2.x 教程(完结版)
查看>>
【Android】定位与解决anr错误记录
查看>>
Virtex6 PCIe 超简版基础概念学习(二)
查看>>
Hive 内建操作符与函数开发——深入浅出学Hive
查看>>
rsyslog学习
查看>>
最近项目中代码管理学习
查看>>
他山之石、能够攻玉 - 我的2015年总结
查看>>
Android 完整开源应用大全,完整开源项目
查看>>
java怎样将一个List传入Oracle存储过程
查看>>
基本的数据类型分析----java.lang.Number类及其子类分析
查看>>
EditPlus v4.5 简体中文
查看>>
&lt;二代測序&gt; 批量下载 NCBI sra 文件
查看>>
web.xml中load-on-startup标签的含义
查看>>
Azure IOT 设备固件更新技巧,看这一篇就够了
查看>>
Excel中输入身份证后3位变成0,怎么办?
查看>>
js学习笔记32----new
查看>>
FFmpeg基础知识之————H264编码profile & level控制
查看>>
PyCharm中按住Alt键,可以选择一个指定列表,然后对这个数列进行操作,比如删除,增加等等...
查看>>
mysql中的case when 与if else
查看>>