原文:[渣译文] SignalR 2.0 系列:SignalR的高频实时通讯

英文渣水平,大伙凑合着看吧……

这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第七篇:SignalR的高频实时通讯

原文:Tutorial: High-Frequency Realtime with SignalR 2.0

概述

本教程演示如何创建一个对象与其他浏览器共享实时状态的应用程序。我们要穿件的应用程序为MoveShape,该MoveShape页面会显示一个Html Div元素,用户可以拖动。并且在用户拖动时,该元素的新位置被发送到服务器,这样其他所有已连接的客户端都会同步更新该元素的位置。

这个教程中使用的应用程序是基于迪米安·爱德华兹的Demo制作的,你可以在这里看到该视频。

本教程将演示从形状的拖动事件引发时如何发送SignalR消息开始,至每个已连接的客户端将接收该消息并更新本地形状的位置。

虽然使用这种方法能够很好的对SignalR的功能进行演示,但这不是一个推荐的编程模型。因为没有限制发送消息的上限,所以客户端和服务器会发送与接收大量的消息,最终导致性能的下降。同时客户端上形状的动画也会被打乱,因为每次接收到位置后形状的位置会由方法立即更新,而不是平滑的移动到新位置。本教程的后面部分将演示如何创建一个定时器功能,限制该消息在客户端和服务器之间发送更新消息的最大频率。本教程中创建的应用程序的最终版本可以在这里下载。

创建项目并添加SignalR和jQuery.UI NuGet包

在本节中,我们使用VS2013来创建项目。

下面演示使用VS2013来创建一个空的ASP.NET应用程序项目,并添加SignalR和jQuery库:

1.在VS2013中创建一个ASP.NET WEB应用程序。

2.在新的ASP.NET项目窗口中,选择空项目并创建。

3.在解决方案资源管理器中,右击该项目,添加一个SignalR集线器类(V2),将该类命名为MoveShapeHub.cs并将其添加到项目中,此步骤创建MoveShapeHub类,并将SignalR脚本和程序集引用添加到该项目中。

注意:您同样可以用库软件包管理器来添加SignalR引用,可以参见前面的教程。

4.使用库软件包管理器来添加jQueryUI。

在程序包管理器控制台中,运行以下命令:

  1. Install-Package jQuery.UI.Combined

该步将jQuery.UI库添加到项目中。

5.在解决方案资源管理器中展开脚本文件夹,你可以看到SignalR和jQuery脚本已经被添加到项目中。

创建基础应用程序

在本节中,我们将创建在客户端中鼠标移动事件触发时将形状的位置发送到服务器的应用程序。至于创建服务器广播该消息给所有其它已连接的客户端的功能,我们将在后面的章节继续。 现在把注意力集中在集线器类的创建上。

1.如果在之前您使用包控制台来添加SignalR,请先添加MoveShapeHub类到项目中。

2.使用下面的代码替换掉MoveShapeHub中的:

  1. using Microsoft.AspNet.SignalR;
  2. using Newtonsoft.Json;
  3.  
  4. namespace MoveShapeDemo
  5. {
  6. public class MoveShapeHub : Hub
  7. {
  8. public void UpdateModel(ShapeModel clientModel)
  9. {
  10. clientModel.LastUpdatedBy = Context.ConnectionId;
  11. // Update the shape model within our broadcaster
  12. Clients.AllExcept(clientModel.LastUpdatedBy).updateShape(clientModel);
  13. }
  14. }
  15. public class ShapeModel
  16. {
  17. // We declare Left and Top as lowercase with
  18. // JsonProperty to sync the client and server models
  19. [JsonProperty("left")]
  20. public double Left { get; set; }
  21. [JsonProperty("top")]
  22. public double Top { get; set; }
  23. // We don't want the client to get the "LastUpdatedBy" property
  24. [JsonIgnore]
  25. public string LastUpdatedBy { get; set; }
  26. }
  27. }

MoveShapeHub是SignalR集线器类的一个实现。在入门教程中,我们使用了客户端直接调用的方法。在这本教程中,客户端将会发送一个包含形状的新的X及Y坐标点对象到服务器上,并且被广播给其他所有已连接的客户端。SignalR将使用JSON来序列化该对象。

我们创建了一个ShapeModel类来作为坐标属性的容器,它包含了形状位置的信息。同时,我们需要指定那些客户单仅仅作为消息的接收端。所以服务器上的对象还包含一个成员跟踪那些客户端的数据被储存。这样,指定的客户端才不会发送它自己的形状坐标数据到服务器上。该成员使用JsonIgnore属性,防止它被序列化并被发送到客户端。

在应用程序启动时启用集线器

1.我们将把设置在应用程序启动时,自动启用集线器映射。在SignalR 2.0中,这是通过增加OWIN启动类来实现的。启动类在类的配置方法中会调用MapSignalR方法,同时启动类会使用Assembly特性来将启动类注册到OWIN的启动处理过程中。

在解决方案资源管理器中,添加一个OWIN启动类,将其命名为Startup并添加。

2.使用以下的代码替换Startup类的内容:

  1. using Microsoft.Owin;
  2. using Owin;
  3.  
  4. [assembly: OwinStartup(typeof(MoveShapeDemo.Startup))]
  5. namespace MoveShapeDemo
  6. {
  7. public class Startup
  8. {
  9. public void Configuration(IAppBuilder app)
  10. {
  11. // Any connection or hub wire up and configuration should go here
  12. app.MapSignalR();
  13. }
  14. }
  15. }

添加客户端

1.接下来,我们将添加客户端。添加一个HTML页面并命名为Default.html到项目中。

2.在解决方案资源管理其中,右击刚刚添加的页面,点击设为起始页。

3.用下面的代码替换HTML页面中的:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SignalR MoveShape Demo</title>
  5. <style>
  6. #shape {
  7. width: 100px;
  8. height: 100px;
  9. background-color: #FF0000;
  10. }
  11. </style>
  12. </head>
  13. <body>
  14. <script src="Scripts/jquery-1.10.2.min.js"></script>
  15. <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
  16. <script src="Scripts/jquery.signalR-2.0.0.js"></script>
  17. <script src="/signalr/hubs"></script>
  18. <script>
  19. $(function () {
  20. var moveShapeHub = $.connection.moveShapeHub,
  21. $shape = $("#shape"),
  22. shapeModel = {
  23. left: 0,
  24. top: 0
  25. };
  26. moveShapeHub.client.updateShape = function (model) {
  27. shapeModel = model;
  28. $shape.css({ left: model.left, top: model.top });
  29. };
  30. $.connection.hub.start().done(function () {
  31. $shape.draggable({
  32. drag: function () {
  33. shapeModel = $shape.offset();
  34. moveShapeHub.server.updateModel(shapeModel);
  35. }
  36. });
  37. });
  38. });
  39. </script>
  40.  
  41. <div id="shape" />
  42. </body>
  43. </html>

注意:请检查代码中所引用的脚本是否同脚本文件夹中的一致:

上面的HTML和JS代码创建了一个红色的Div,id为Shape。在Shape拖动时,将触发它的drag事件,并将Div的位置发送给服务器。

4.按下F5启动应用程序,复制页面的URL并打开一个新的浏览器,粘贴并打开,拖动一个浏览器的窗口中的形状,另一个浏览器的形状位置也将同步进行更新。

添加客户端循环

由于每一次移动鼠标都会发送位置信息到服务器端并进行广播,这将大大影响网络流量及程序的性能。我们需要对客户端的消息进行节流限制。我们将使用JS的setIntrval函数来设置一个固定速度的循环方法,使用该方法以固定的频率将形状的位置信息发送到服务器。这个循环是一个"游戏循环",一个被反复调用的函数,用于驱动所有需要定期检查或其他模拟功能的方法。

1.用以下代码更新HTML页的内容:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SignalR MoveShape Demo</title>
  5. <style>
  6. #shape {
  7. width: 100px;
  8. height: 100px;
  9. background-color: #FF0000;
  10. }
  11. </style>
  12. </head>
  13. <body>
  14. <script src="Scripts/jquery-1.10.2.min.js"></script>
  15. <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
  16. <script src="Scripts/jquery.signalR-2.0.1.js"></script>
  17. <script src="/signalr/hubs"></script>
  18. <script>
  19. $(function () {
  20. var moveShapeHub = $.connection.moveShapeHub,
  21. $shape = $("#shape"),
  22. // Send a maximum of 10 messages per second
  23. // (mouse movements trigger a lot of messages)
  24. messageFrequency = 10,
  25. // Determine how often to send messages in
  26. // time to abide by the messageFrequency
  27. updateRate = 1000 / messageFrequency,
  28. shapeModel = {
  29. left: 0,
  30. top: 0
  31. },
  32. moved = false;
  33. moveShapeHub.client.updateShape = function (model) {
  34. shapeModel = model;
  35. $shape.css({ left: model.left, top: model.top });
  36. };
  37. $.connection.hub.start().done(function () {
  38. $shape.draggable({
  39. drag: function () {
  40. shapeModel = $shape.offset();
  41. moved = true;
  42. }
  43. });
  44. // Start the client side server update interval
  45. setInterval(updateServerModel, updateRate);
  46. });
  47. function updateServerModel() {
  48. // Only update server if we have a new movement
  49. if (moved) {
  50. moveShapeHub.server.updateModel(shapeModel);
  51. moved = false;
  52. }
  53. }
  54. });
  55. </script>
  56.  
  57. <div id="shape" />
  58. </body>
  59. </html>

我们创建了updateServerModel方法来使用一个固定频率将位置信息发送给服务器。当move标志位变动时,该函数才将信息传送给服务器。

2.按下F5运行,同样复制一个浏览器窗口,拖动一个窗口中的形状并观察另一个。这一次我们发送的数据将被节流,所以你可以看到动画将不如之前的那样平滑。

添加服务器循环

在目前的应用中,每当服务器接收到新消息时,都会将它们广播到所有客户端上。同客户端的问题一样:消息总是发送而不是在需要时才发送,并且连接可能被结果淹没。本节介绍如何更新服务器代码以实现节流传出消息的速率定时器。

1.使用以下代码更新MoveShapeHub:

  1. using System;
  2. using System.Threading;
  3. using Microsoft.AspNet.SignalR;
  4. using Newtonsoft.Json;
  5.  
  6. namespace MoveShapeDemo
  7. {
  8. public class Broadcaster
  9. {
  10. private readonly static Lazy<Broadcaster> _instance =
  11. new Lazy<Broadcaster>(() => new Broadcaster());
  12. // We're going to broadcast to all clients a maximum of 25 times per second
  13. private readonly TimeSpan BroadcastInterval =
  14. TimeSpan.FromMilliseconds();
  15. private readonly IHubContext _hubContext;
  16. private Timer _broadcastLoop;
  17. private ShapeModel _model;
  18. private bool _modelUpdated;
  19. public Broadcaster()
  20. {
  21. // Save our hub context so we can easily use it
  22. // to send to its connected clients
  23. _hubContext = GlobalHost.ConnectionManager.GetHubContext<MoveShapeHub>();
  24. _model = new ShapeModel();
  25. _modelUpdated = false;
  26. // Start the broadcast loop
  27. _broadcastLoop = new Timer(
  28. BroadcastShape,
  29. null,
  30. BroadcastInterval,
  31. BroadcastInterval);
  32. }
  33. public void BroadcastShape(object state)
  34. {
  35. // No need to send anything if our model hasn't changed
  36. if (_modelUpdated)
  37. {
  38. // This is how we can access the Clients property
  39. // in a static hub method or outside of the hub entirely
  40. _hubContext.Clients.AllExcept(_model.LastUpdatedBy).updateShape(_model);
  41. _modelUpdated = false;
  42. }
  43. }
  44. public void UpdateShape(ShapeModel clientModel)
  45. {
  46. _model = clientModel;
  47. _modelUpdated = true;
  48. }
  49. public static Broadcaster Instance
  50. {
  51. get
  52. {
  53. return _instance.Value;
  54. }
  55. }
  56. }
  57.  
  58. public class MoveShapeHub : Hub
  59. {
  60. // Is set via the constructor on each creation
  61. private Broadcaster _broadcaster;
  62. public MoveShapeHub()
  63. : this(Broadcaster.Instance)
  64. {
  65. }
  66. public MoveShapeHub(Broadcaster broadcaster)
  67. {
  68. _broadcaster = broadcaster;
  69. }
  70. public void UpdateModel(ShapeModel clientModel)
  71. {
  72. clientModel.LastUpdatedBy = Context.ConnectionId;
  73. // Update the shape model within our broadcaster
  74. _broadcaster.UpdateShape(clientModel);
  75. }
  76. }
  77. public class ShapeModel
  78. {
  79. // We declare Left and Top as lowercase with
  80. // JsonProperty to sync the client and server models
  81. [JsonProperty("left")]
  82. public double Left { get; set; }
  83. [JsonProperty("top")]
  84. public double Top { get; set; }
  85. // We don't want the client to get the "LastUpdatedBy" property
  86. [JsonIgnore]
  87. public string LastUpdatedBy { get; set; }
  88. }
  89.  
  90. }

上面的代码新增了Broadcaster类,它使用.Net框架中的Timer类来对发送消息进行节流。

由于集线器本身是暂时存在的(每次需要时才创建),Broadcaster被创建为一个单例。使用了延迟初始化(.Net4中新增功能),来推迟其创建时间直到需要它为止。这是为了确保在计时器开始之前就有集线器的实例被成功创建完毕。

调用客户端的updateShape功能被移出集线器的updateModel方法,所有消息传入后它将不再立即被调用。相反,需要发送至客户端的消息会以每秒25次的频率进行发送。Broadcaster类中的_broadcastLoop计时器来承担发送频率的管理功能。

最终,集线器并不直接调用客户端方法,Broadcaster类需要使用GlobalHost来获得一个引用当前正在运行的操作集线器(_hubContext)。

2.按F5启动应用程序,复制窗口并拖动,将不会同上一节中的效果有太大差别。但在后台,我们已经对发送到客户端的消息进行了节流限制。

为客户端加入流畅的动画效果

这个应用程序已经很完善了,但我们还需要做进一步的改进。客户端的形状移动是由接收到服务器消息而进行的,我们将使用jQuery UI库的animate功能来优化形状的移动效果,而不是直接使用服务器提供的新位置来改变形状的当前位置。

1.使用下面的代码更新HTML页面:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>SignalR MoveShape Demo</title>
  5. <style>
  6. #shape {
  7. width: 100px;
  8. height: 100px;
  9. background-color: #FF0000;
  10. }
  11. </style>
  12. </head>
  13. <body>
  14. <script src="Scripts/jquery-1.10.2.min.js"></script>
  15. <script src="Scripts/jquery-ui-1.10.4.min.js"></script>
  16. <script src="Scripts/jquery.signalR-2.0.0.js"></script>
  17. <script src="/signalr/hubs"></script>
  18. <script>
  19. $(function () {
  20. var moveShapeHub = $.connection.moveShapeHub,
  21. $shape = $("#shape"),
  22. // Send a maximum of 10 messages per second
  23. // (mouse movements trigger a lot of messages)
  24. messageFrequency = 10,
  25. // Determine how often to send messages in
  26. // time to abide by the messageFrequency
  27. updateRate = 1000 / messageFrequency,
  28. shapeModel = {
  29. left: 0,
  30. top: 0
  31. },
  32. moved = false;
  33. moveShapeHub.client.updateShape = function (model) {
  34. shapeModel = model;
  35. // Gradually move the shape towards the new location (interpolate)
  36. // The updateRate is used as the duration because by the time
  37. // we get to the next location we want to be at the "last" location
  38. // We also clear the animation queue so that we start a new
  39. // animation and don't lag behind.
  40. $shape.animate(shapeModel, { duration: updateRate, queue: false });
  41. };
  42. $.connection.hub.start().done(function () {
  43. $shape.draggable({
  44. drag: function () {
  45. shapeModel = $shape.offset();
  46. moved = true;
  47. }
  48. });
  49. // Start the client side server update interval
  50. setInterval(updateServerModel, updateRate);
  51. });
  52. function updateServerModel() {
  53. // Only update server if we have a new movement
  54. if (moved) {
  55. moveShapeHub.server.updateModel(shapeModel);
  56. moved = false;
  57. }
  58. }
  59. });
  60. </script>
  61.  
  62. <div id="shape" />
  63. </body>
  64. </html>

上面的代码将使用动画来把形状移动到新的位置上,在此例中,我们使用100毫秒作为动画间隔。

2.按下F5启动应用程序,复制窗口并拖动,你可以看到形状的移动比之前更流畅。形状每次移动是随着时间进行插补而不是每当有消息传入就立即更新一次。

下一步

在本教程中,您学习了如何编写客户端同服务器端高频实时通讯的SignalR应用,这种通信模式被经常用来开发网络游戏。比如:the ShootR game created with SignalR

作者:帕特里克·弗莱彻 -帕特里克·弗莱彻是ASP.NET开发团队的程序员,作家,目前正在SignalR项目工作。

[渣译文] SignalR 2.0 系列:SignalR的高频实时通讯的更多相关文章

  1. [渣译文] SignalR 2.0 系列: SignalR 自托管主机

    原文:[渣译文] SignalR 2.0 系列: SignalR 自托管主机 英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.N ...

  2. [渣译文] SignalR 2.0 系列: 开始使用SignalR 2.0

    原文:[渣译文] SignalR 2.0 系列: 开始使用SignalR 2.0 英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP ...

  3. [渣译文] SignalR 2.0 系列: 支持的平台

    原文:[渣译文] SignalR 2.0 系列: 支持的平台 英文渣水平,大伙凑合着看吧,并不是逐字翻译的…… 这是微软官方SignalR 2.0教程Getting Started with ASP. ...

  4. [渣译文] SignalR 2.0 系列: SignalR简介

    原文:[渣译文] SignalR 2.0 系列: SignalR简介 英文渣水平,大伙凑合着看吧,并不是逐字翻译的…… 这是微软官方SignalR 2.0教程Getting Started with ...

  5. [渣译文] SignalR 2.0 系列:SignalR的服务器广播

    英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第八篇:SignalR的服务器广 ...

  6. SignalR 2.0 系列:SignalR的服务器广播

    英文渣水平,大伙凑合着看吧…… 这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第八篇:SignalR的服务器广 ...

  7. SignalR 2.0 系列: SignalR简介

    SignalR 2.0 系列: SignalR简介 英文渣水平,大伙凑合着看吧,并不是逐字翻译的…… 这是微软官方SignalR 2.0教程Getting Started with ASP.NET S ...

  8. SignalR 2.0 系列: 开始使用SignalR 2.0

    这是微软官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻译,这里是第四篇:开始使用SignalR 2.0 原文:Getting S ...

  9. 用SignalR 2.0开发客服系统[系列1:实现群发通讯]

    前言 交流群:195866844 先说一下我为什么会写这个博客吧,(首先说一下,我是一个小菜鸟,讲的不好请指导 - -,)  前段时间公司的项目涉及到在B/S上使用即时通讯,(其实就是做一个B/S的客 ...

随机推荐

  1. 【大话QT之十七】Jenkins介绍及安装使用文档(与Git集成)

    文章文件夹结构例如以下: 1> Jenkins与Git相关介绍 2> Jenkins部署安装 3> Gitblit部署安装 4> Jenkins与Git集成使用 5> 项 ...

  2. 实现TextView 文字排版,分散两端对齐

    參考:http://www.cnblogs.com/lcyty/p/3265335.html 方法一:使用HTML TextView textview=(TextView)findViewbyId(R ...

  3. LaTeX中表格多行显示的最简单设置方法

    这事实上是一个非常easy的问题,可是这两天发现我之前的解决方式太麻烦了.简介一下这样的最简单的方法: 之前设置多行显示的时候,用类似于以下这样的方法进行多行显示: \begin{table} \ne ...

  4. linux LNMP自动安装脚本

    #!/bin/bashsoft_dir="/home/soft"config_dir="/home/config"httpd="httpd-2.0.5 ...

  5. java的提取与替换操作

    public class Demo02 { public static void main(String args[]){ String str = "java 技术学习班  2007032 ...

  6. Android开发之按键、触摸屏和手势输入专业压力測试方法

    按键输入.触摸屏输入和手势笔画输入等功能是Android开发的基本功能.其稳定性和健壮性对移动应用系统开发很重要.按键.触摸屏和手势输入专业压力測试方法能够使用Monkey,相应用程序进行压力測试,检 ...

  7. jenkins 安装 SVN Publisher 后向 svn 提交代码报错: E170001: Authentication required for...

    问题描写叙述 安装并启动 jenkins 后,加入了 SVN Publisher 插件,然后在构建任务的"构建后操作"操作中加入了"Publish to Subversi ...

  8. 我的Android进阶之旅------>经典的大牛博客推荐(排名不分先后)!!

    本文来自:http://blog.csdn.net/ouyang_peng/article/details/11358405 今天看到一篇文章,收藏了很多大牛的博客,在这里分享一下 谦虚的天下 柳志超 ...

  9. android中listview分页载入数据

    前段时间做的新浪微博项目一直想实现listview分页载入数据,今天最终实现了,哈哈!感觉挺好的,今天又写了个demo给大家分享下. 首先说下listview的优化方案,这也是面试中常考的题目.优化方 ...

  10. Windows Phone开发(40):漫谈关键帧动画之中篇

    原文:Windows Phone开发(40):漫谈关键帧动画之中篇 一.DiscreteDoubleKeyFrame 离散型关键帧动画,重点,我们理解一下"离散"的意思,其实你查一 ...