ASP.NET SignalR 2.0 SignalR的高頻實時通訊
本教程演示如何建立一個物件與其他瀏覽器共享實時狀態的應用程式。我們要穿件的應用程式為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。
在程式包管理器控制檯中,執行以下命令:
view source print ?1.
Install-Package jQuery.UI.Combined
該步將jQuery.UI庫新增到專案中。
5.在解決方案資源管理器中展開指令碼資料夾,你可以看到SignalR和jQuery指令碼已經被新增到專案中。
建立基礎應用程式
在本節中,我們將建立在客戶端中滑鼠移動事件觸發時將形狀的位置傳送到伺服器的應用程式。至於建立伺服器廣播該訊息給所有其它已連線的客戶端的功能,我們將在後面的章節繼續。 現在把注意力集中在集線器類的建立上。
1.如果在之前您使用包控制檯來新增SignalR,請先新增MoveShapeHub類到專案中。
2.使用下面的程式碼替換掉MoveShapeHub中的:
view source print ?01.
using
Microsoft.AspNet.SignalR;
02.
using
Newtonsoft.Json;
03.
04.
namespace
MoveShapeDemo
05.
{
06.
public
class
MoveShapeHub : Hub
07.
{
08.
public
void
UpdateModel(ShapeModel clientModel)
09.
{
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類的內容:
view source print ?01.
using
Microsoft.Owin;
02.
using
Owin;
03.
04.
[assembly: OwinStartup(
typeof
(MoveShapeDemo.Startup))]
05.
namespace
MoveShapeDemo
06.
{
07.
public
class
Startup
08.
{
09.
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頁面中的:
view source print ?01.
<!DOCTYPE html>
02.
<
html
>
03.
<
head
>
04.
<
title
>SignalR MoveShape Demo</
title
>
05.
<
style
>
06.
#shape {
07.
width: 100px;
08.
height: 100px;
09.
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頁的內容:
view source print ?01.
<!DOCTYPE html>
02.
<
html
>
03.
<
head
>
04.
<
title
>SignalR MoveShape Demo</
title
>
05.
<
style
>
06.
#shape {
07.
width: 100px;
08.
height: 100px;
09.
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:
view source print ?01.
using
System;
02.
using
System.Threading;
03.
using
Microsoft.AspNet.SignalR;
04.
using
Newtonsoft.Json;
05.
06.
namespace
MoveShapeDemo
07.
{
08.
public
class
Broadcaster
09.
{
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.