SignalR的实时高频通讯
SignalR的实时高频通讯
第五章SignalR的实时高频通讯
概述:本例子演示了如果创建一个对象与其他浏览器共享实时状态的应用程序。我们要创建的应用程序为“MoveShape”,该MoveShape页面会显示一个Html Div元素,用户可以拖动,并且在用户拖动时,该元素的新位置将被发送到服务器,这样,其他所有已连接的客户端都会同步更新该元素的位置。
在这个例子中使用的应用程序是基于迪米安·爱德华兹的Demo制作的,你可以在这里看到该视频。
本例子将演示从形状的拖动事件引发时,如何发送SignalR消息开始,至每个已连接的客户端将收到该消息,并且更新本地形状的位置。
虽然使用这种方法能够很好地对SignalR的高频实时功能进行演示,但这不是一个推荐的编程模型。因为:
1)没有限制发送消息上限,会令客户端和服务器端发送和接收大量的消息,最终导致性能 下降。
2)每次接收到新位置后,形状的位置会由方法立即更新,而不是平滑地移动到新位置,所 以客户端上形状动画也会被打乱。
本例子的后面部分将演示如何创建一个定时器的功能,限制该消息在客户端和服务器端之间的发送更新消息的最大频率。本例子创建的应用程序的最终版可以在这里下载。
1.创建项目并添加SignalR和jQuery.UI NuGet包:
1)在VS2013中创建一个Asp.Net Web应用程序:
2)在新的Asp.Net项目窗口中,选择空项目并且创建:
3)在解决方案资源管理器中,右击该项目,添加一个SignalR集显器类(V2),该类命名为MoveShapeHub.cs,并将其添加到项目中,此步骤创建MoveShapeHub类,并将SignalR脚本和程序集引用添加到该项目中。
注意:您同样可以用库软件包管理器来添加SignalR引用,可以参考前面的例子。
4)使用库软件包管理器来添加jQueryUI。
在程序包管理控制台中,运行以下命令:
Install-Package jQuery.UI.Combined |
该步骤将jQuery.UI库添加到项目中。
5)在解决方案资源管理器中展开脚本文件夹,您可以看到SignalR和jQuery脚本已经被添加到项目中:
2.创建基础应用程序:
在本节中,我们将创建在客户端中鼠标移动事件触发时,形状的位置发送到服务器的应用程序。至于创建服务器广播该消息给所有其他已连接的客户端的功能,我们将在后面的章节中继续讲解,现在,请把注意力集中在集线器类的创建上。
1)如果在之前您使用包控制台来添加SignalR,请先添加MoveShapeHub类到项目中。
2)使用下面的代码替代换掉的MoveShapeHub中的:
using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class MoveShapeHub : Hub { public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } } |
MoveShapeHub是SignalR集线器类的一个实现。在入门教程中,我们使用了客户端直接调用的方法。在这本教程中,客户端将会发送一个包含形 状的新的X及Y坐标点对象到服务器上,并且被广播给其他所有已连接的客户端。SignalR将使用JSON来序列化该对象。
我们创建了一个ShapeModel类来作为坐标属性的容器,它包含了形状位置的信息。同时,我们需要指定那些客户仅作为消息的接收端。所以服务器上 的对象还包含一个成员跟踪那些客户端的数据被储存。这样,指定的客户端才不会发送它自己的形状坐标数据到服务器上。该成员使用JsonIgnore属性, 防止它被序列化并被发送到客户端。
3.在应用程序启动时启用集线器:
1) 我们将把设置在应用程序启动时,自动启用集线器映射。在SignalR2.0中,这是通过增加OWIN启动类来实现的。启动类的配置方法中会调用MapSigalR方法,同时启动类会使用Assembly特性来将启动类注册到OWIN的启动处理过程中。
在解决方案资源管理器中,添加一个OWIN启动类,将其命名为Startup并添加。
2)使用一下的代码替换Startup类的内容:
using Microsoft.Owin; using Owin; [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))] namespace MoveShapeDemo { public class Startup { public void Configuration(IAppBuilder app) { app.MapSignalR(); } } } |
4.添加客户端:
1)接下来,我们将添加客户端。添加一个Html页面,并且命名为Default.html带项目中。
2)在解决方案资源管理器中,右击刚刚添加的页面,点击设为起始页。
3)用下面的代码替换Html页面中的:
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style>#shape { width: 100px;height: 100px;}</style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.0.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), shapeModel = { left: 0, top: 0 }; moveShapeHub.client.updateShape = function (model) { shapeModel = model; $shape.css({ left: model.left, top: model.top }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moveShapeHub.server.updateModel(shapeModel); } }); }); }); </script> <div id="shape" /> </body> </html> |
意:请检查代码中所引用的脚本是否同脚本文件夹中的一致:
上面的Html和JS代码创建了一个红色的Div,id为Shape。在Shape拖动时,将触发它的drag事件,并将Div的位置发送给服务器。
4) 按下F5启动应用程序,复制页面的URL并打开一个新的浏览器,粘贴并打开,拖动一个浏览器的窗口中的形状,另一个浏览器的形状位置也将同步进行更新。
5.添加服务器循环:
在目前的应用中,每当服务器接收到新的消息时,都会将它们广播到所有的客户端上。同客户端的问题一样:消息总是不断发送,而不是在需要时才发送,并且可能导致连接被结果淹没。以下服务器代码就是为了解决这一问题:已实现节流出书消息的速率定时器(减少并发,防止不断并发而导致连接的实例没创建,就要发送,导致发送失败,也可以防止并发连接和并发推送消息太多,导致同一时间资源占用过多,)。
1)使用以下代码更新MoveShapeHub:
using System; using System.Threading; using Microsoft.AspNet.SignalR; using Newtonsoft.Json; namespace MoveShapeDemo { public class Broadcaster { private readonly static Lazy<Broadcaster> _instance = new Lazy<Broadcaster>(() => new Broadcaster()); // We're going to broadcast to all clients a maximum of 25 times per second private readonly TimeSpan BroadcastInterval = TimeSpan.FromMilliseconds(40); private readonly IHubContext _hubContext; private Timer _broadcastLoop; private ShapeModel _model; private bool _modelUpdated; public Broadcaster() { // Save our hub context so we can easily use it // to send to its connected clients _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>(); _model = new ShapeModel(); _modelUpdated = false; // Start the broadcast loop _broadcastLoop = new Timer(BroadcastShape, null, BroadcastInterval, BroadcastInterval); } public void BroadcastShape(object state) { // No need to send anything if our model hasn't changed if (_modelUpdated) { // This is how we can access the Clients property // in a static hub method or outside of the hub entirely _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model); _modelUpdated = false; } |
} public void UpdateShape(ShapeModel clientModel) { _model = clientModel; _modelUpdated = true; } public static Broadcaster Instance { get{ return _instance.Value; }} } public class MoveShapeHub : Hub { // Is set via the constructor on each creation private Broadcaster _broadcaster; public MoveShapeHub() : this(Broadcaster.Instance){} public MoveShapeHub(Broadcaster broadcaster) { _broadcaster = broadcaster; } public void UpdateModel(ShapeModel clientModel) { clientModel.LastUpdatedBy = Context.ConnectionId; // Update the shape model within our broadcaster _broadcaster.UpdateShape(clientModel); } } public class ShapeModel { // We declare Left and Top as lowercase with // JsonProperty to sync the client and server models [JsonProperty("left")] public double Left { get; set; } [JsonProperty("top")] public double Top { get; set; } // We don't want the client to get the "LastUpdatedBy" property [JsonIgnore] public string LastUpdatedBy { get; set; } } } |
上面的代码新增了Broadcaster类,它使用.Net框架中的Timer类来对发送消息进行节流。
由于集线器本身是暂时存在的(每次都是需要时才创建),Broadcaster被创建为一个单例。使用率延迟初始化(.Net4中新增功能),来推迟其创建时间,直到需要它为止。这是为了确保在计时器开始之前,就有集线器的实例被成功创建完毕。
调用客户端的updateShape功能,被移出集线器的updateModel方法,所有消息传入后,它将不再立即被调用。相反,需要发送至客户端的消息会已每秒25次的频率进行并发。Broadcaster类中的_broadcastLoop计时器来承担发送频率的管理功能。最终,集线器并不直接调用客户端方法,Broadcaster类需要使用GlobalHost来获得一个引用当前正在运行的操作集线器(_hubContext)。
2)按F5启动应用程序,复制窗口并拖动,将不会同上一节中的效果有太大区别。但在后台,我们已经对发送到客户端的消息进行了节流限制。
6.为客户端加入流畅的动画效果:
这个应用程序到这里,已经很完善了,但我们还能有进一步的改进。客户端的形状移动是由接受到服务器的消息而进行的。我们将使用jQueryUI库的animate功能来优化形状的移动效果,而不是直接使用服务器提供的新位置来直接改变形状的当前位置。
1)使用下面代码更新Html页面:
<!DOCTYPE html> <html> <head> <title>SignalR MoveShape Demo</title> <style> |
|
#shape { width: 100px; height: 100px; } </style> </head> <body> <script src="Scripts/jquery-1.10.2.min.js"></script> <script src="Scripts/jquery-ui-1.10.4.min.js"></script> <script src="Scripts/jquery.signalR-2.0.0.js"></script> <script src="/signalr/hubs"></script> <script> $(function () { var moveShapeHub = $.connection.moveShapeHub, $shape = $("#shape"), // Send a maximum of 10 messages per second // (mouse movements trigger a lot of messages) messageFrequency = 10, // Determine how often to send messages in // time to abide by the messageFrequency updateRate = 1000 / messageFrequency, shapeModel = { left: 0,top: 0 }, moved = false; moveShapeHub.client.updateShape = function (model) { shapeModel = model; // Gradually move the shape towards the new location (interpolate) // The updateRate is used as the duration because by the time // we get to the next location we want to be at the "last" location // We also clear the animation queue so that we start a new // animation and don't lag behind. $shape.animate(shapeModel, { duration: updateRate, queue: false }); }; $.connection.hub.start().done(function () { $shape.draggable({ drag: function () { shapeModel = $shape.offset(); moved = true; } }); |
|
// Start the client side server update interval setInterval(updateServerModel, updateRate); }); function updateServerModel() { // Only update server if we have a new movement if (moved) { moveShapeHub.server.updateModel(shapeModel); moved = false; } } }); </script> <div id="shape" /> </body> </html> |
上面的代码将使用动画来把形状移动到新的位置上,在本例子中,我们使用100毫秒作为动画间隔。
2)按下F5启动应用程序,复制窗口并且拖动,您可以看到形状的移动比之前更流畅,形状每次移动是随着时间进行补插而不是每当有新消息传入就立即更新一次。
7.下一步:
在本例子中,您学习了如何编写客户端同服务器端高频实时通讯的SignalR应用,这种通信模式被经常用来开发网络游戏。比如:the ShootR game created with SignalR。
SignalR的实时高频通讯的更多相关文章
- 第五章SignalR的实时高频通讯
第五章SignalR的实时高频通讯 概述:本例子演示了如果创建一个对象与其他浏览器共享实时状态的应用程序.我们要创建的应用程序为“MoveShape”,该MoveShape页面会显示一个Html Di ...
- SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 SignalR 简单示例 通过三个DEMO学会SignalR的三种实现方式 SignalR推送框架两个项目永久连接通讯使用 SignalR 集线器简单实例2 用SignalR创建实时永久长连接异步网络应用程序
SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论 异常汇总:http://www ...
- Asp.net SignalR 让实时通讯变得如此简单
巡更项目中,需要发送实时消息,以及需要任务开始提醒,于是便有机会接触到SignalR,在使用过程中,发现用SignalR实现通信非常简单,下面我思明将从三个方面分享一下: 一.SignalR是什么 A ...
- RDIFramework.NET敏捷开发框架通过SignalR技术整合即时通讯(IM)
1.引言 即时通讯(IM)是RDIFramework.NET敏捷开发框架全新提供的一个基于Web的即时通讯.内部聊天沟通的工具.界面美观大方对于框架内部进行消息的沟通非常方便.基于RDIFramewo ...
- 通过SignalR技术整合即时通讯(IM)在.NET中应用落地
1.引言 即时通讯(IM)是RDIFramework.NET敏捷开发框架全新提供的一个基于Web的即时通讯.内部聊天沟通的工具.界面美观大方对于框架内部进行消息的沟通非常方便.基于RDIFramewo ...
- 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)
我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...
- 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)
今天没有延续上一篇讲的内容,穿插一段小插曲,WebSocket 实时数据通讯同步的问题,今天我们并不是很纯粹地讲 WebSocket 相关知识,我们通过 WebGL 3D 拓扑图来呈现一个有趣的 De ...
- 通过 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)
我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...
- 教程-Delphi MSComm 实时串口通讯
Delphi MSComm 实时串口通讯 MSComm控件具有丰富的与串口通信密切相关的属性,提供了对串口进行的多种操作,进而使串行通信变得十分简便.MSComm的控件属性较多,常用的属性如下:1) ...
随机推荐
- MAC 10.10 apache 服务器配置
mac中自带了apache服务器, 如果需要在mac上使用apache服务器, 只需要配置并启动服务器即可. mac 10.10 中自带的apache版本是 2.4 mac 10.9 中自带的apac ...
- Hadoop之—— CentOS Warning: $HADOOP_HOME is deprecated解
转载请注明出处:http://blog.csdn.net/l1028386804/article/details/46389499 启动Hadoop时报了一个警告信息.我安装的Hadoop版本号是ha ...
- 浅谈web网站架构演变过程(转)
前言 我们以javaweb为例,来搭建一个简单的电商系统,看看这个系统可以如何一步步演变. 该系统具备的功能: 用户模块:用户注册和管理 商品模块:商品展示和管理 交易模块:创建交易和管理 阶 ...
- flask+gevent+gunicorn+nginx 初试
1.安装flask pip install flask 2.安装gevent pip install gevent 3.安装gunicorn pip install gunicorn 版本信息例如以下 ...
- hadoop得知;block数据块;mapreduce实现样例;UnsupportedClassVersionError变态;该项目的源代码相关联
对于开源的东西.特别是刚出来不久.我认为最好的学习方法是能够看到源代码,doc,样品测试 为了方便查看源代码,导入与项目相关的源代码 watermark/2/text/aHR0cDovL2Jsb2cu ...
- SharePoint使用BCS开发你第一个应用程序(三)
SharePoint使用BCS开发你第一个应用程序(三) 创建外部内容类型. 创建外部内容类型有三种不同方式: 1. 在记事本上手写XML代码(不推荐). 2. 使用SharePoin ...
- 跑Java -jar somefile.jar时会发生什么(一个)
最近阅读JVM源代码.一些想法写Blog分享.于是,他开了这么一个新课题. 第一篇文章取名字的时候让我很困惑,我代码的阅读是从Launcher開始入手的,也就是Java.exe(假设是windows平 ...
- LNK快捷方式漏洞利用方式 exp制作教程
前言windows的shell32在处理控制面板程序的快捷方式文件时,存在一个漏洞,能够载入硬盘上的随意DLL文件,就可以运行随意代码. 漏洞文件的生成到“控制面板”以下,右键点“显示”,点“创建快捷 ...
- Team Foundation Server 2015使用教程--默认团队成员连接tfs及checkin操作
- Oracle拉进sqlserver表声明的建设
我们将Oracle数据被同步到sqlserver时间,早餐在sqlserver表中的端构造. 我们是不同步的复杂领域,只考虑以下四种数据类型. Oracle到SQLServer做的映射: int -& ...