[渣譯文] SignalR 2.0 系列:SignalR的高頻實時通信

原文: [渣譯文] SignalR 2.0 系列:SignalR的高頻實時通信

英文渣水平,大夥湊合着看吧……css

這是微軟官方SignalR 2.0教程Getting Started with ASP.NET SignalR 2.0系列的翻譯,這裏是第七篇:SignalR的高頻實時通信html

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

概述

本教程演示如何建立一個對象與其餘瀏覽器共享實時狀態的應用程序。咱們要穿件的應用程序爲MoveShape,該MoveShape頁面會顯示一個Html Div元素,用戶能夠拖動。而且在用戶拖動時,該元素的新位置被髮送到服務器,這樣其餘全部已鏈接的客戶端都會同步更新該元素的位置。程序員

這個教程中使用的應用程序是基於迪米安·愛德華茲的Demo製做的,你能夠在這裏看到該視頻。web

本教程將演示從形狀的拖動事件引起時如何發送SignalR消息開始,至每一個已鏈接的客戶端將接收該消息並更新本地形狀的位置。編程

雖然使用這種方法可以很好的對SignalR的功能進行演示,但這不是一個推薦的編程模型。由於沒有限制發送消息的上限,因此客戶端和服務器會發送與接收大量的消息,最終致使性能的降低。同時客戶端上形狀的動畫也會被打亂,由於每次接收到位置後形狀的位置會由方法當即更新,而不是平滑的移動到新位置。本教程的後面部分將演示如何建立一個定時器功能,限制該消息在客戶端和服務器之間發送更新消息的最大頻率。本教程中建立的應用程序的最終版本能夠在這裏下載。瀏覽器

建立項目並添加SignalR和jQuery.UI NuGet包

在本節中,咱們使用VS2013來建立項目。服務器

下面演示使用VS2013來建立一個空的ASP.NET應用程序項目,並添加SignalR和jQuery庫:網絡

1.在VS2013中建立一個ASP.NET WEB應用程序。app

2.在新的ASP.NET項目窗口中,選擇空項目並建立。

3.在解決方案資源管理器中,右擊該項目,添加一個SignalR集線器類(V2),將該類命名爲MoveShapeHub.cs並將其添加到項目中,此步驟建立MoveShapeHub類,並將SignalR腳本和程序集引用添加到該項目中。

注意:您一樣能夠用庫軟件包管理器來添加SignalR引用,能夠參見前面的教程。

4.使用庫軟件包管理器來添加jQueryUI。

在程序包管理器控制檯中,運行如下命令:

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 }
16     

添加客戶端

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(40); 
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項目工做。

相關文章
相關標籤/搜索