SignalR 是一個集成的客戶端與服務器庫,基於瀏覽器的客戶端和基於 ASP.NET 的服務器組件可以借助它來進行雙向多步對話。 換句話說,該對話可不受限制地進行單個無狀態請求/響應數據交換;它將繼續,直到明確關閉。 對話通過永久連接進行,允許客戶端向服務器發送多個消息,並允許服務器做出相應答復,值得注意的是,還允許服務器向客戶端發送異步消息。它和AJax類似,都是基於現有的技術。本身是一個復合體。一般情況下,SignalR會使用Javascript的長輪詢( long polling),實現客戶端和服務端通信。在WebSockets出現以后,SignalR也支持WebSockets通信。當然SignalR也使用了服務端的任務並行處理技術以提高服務器的擴展性。它的目標整個 .NET Framework 平台,它也不限 Hosting 的應用程序,而且還是跨平台的開源項目,支持Mono 2.10+,覺得它變成是 Web API 的另一種實作選擇,但是它在服務端處理聯機的功能上比 ASP.NET MVC 的 Web API 要強多了,更重要的是,它可以在 Web Form 上使用。
SignalR 內的客戶端庫 (.NET/JavaScript) 提供了自動管理的能力,開發人員只需要直接使用 SignalR 的 Client Library 即可,同時它的 JavaScript 庫可和 jQuery 完美整合,因此能直接與像 jQuery 或 Knockout.js 一起使用。
SignalR內部有兩類對象:
· Persistent Connection(HTTP持久鏈接):持久性連接,用來解決長時間連接的能力,而且還可以由客戶端主動向服務器要求數據,而服務器端也不需要實現太多細節,只需要處理 PersistentConnection 內所提供的五個事件:OnConnected, OnReconnected, OnReceived, OnError 和 OnDisconnect 即可。
· Hub:信息交換器,用來解決 realtime 信息交換的功能,服務器端可以利用 URL 來注冊一個或多個 Hub,只要連接到這個 Hub,就能與所有的客戶端共享發送到服務器上的信息,同時服務器端可以調用客戶端的腳本,不過它背后還是不離 HTTP 的標准,所以它看起來神奇,但它並沒有那么神奇,只是 JavaScript 更強,強到可以用像 eval() 或是動態解釋執行的方式,允許 JavaScript 能夠動態的加載與執行方法調用而己。
SignalR 將整個交換信息的行為封裝得非常漂亮,客戶端和服務器全部都使用 JSON 來溝通,在服務器端聲明的所有 hub 的信息,都會一般生成 JavaScript 輸出到客戶端,.NET 則是依賴 Proxy 來生成代理對象,這點就和 WCF/.NET Remoting 十分類似,而 Proxy 的內部則是將 JSON 轉換成對象,以讓客戶端可以看到對象。
下面我們來針對Persistent Connection和Hub 做個Demo試試:
新建一個ASP.NET MVC項目MvcApplicationSignalR,通過Nuget添加SignalR的包。
新建一個類MyConnection 繼承自 PersistentConnection ,引用SignalR命名空間,重寫OnReceivedAsync 的方法,並要求 SignalR 對傳入的信息做廣播
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SignalR;
using System.Threading.Tasks;
namespace MvcApplicationSignalR
{
public class MyConnection : PersistentConnection
{
protected override Task OnReceivedAsync(IRequest request, string connectionId, string data)
{
// Broadcast data to all clients
data = string.Format("數據是:{0} 時間是:{1}", data, DateTime.Now.ToString());
return Connection.Send(connectionId, data);
}
}
}
接着在 Global.asax 中加入對應路由信息,這會由 SignalR 的路由表來處理 Metadata 的輸出工作,紅色部分代碼:
protected void Application_Start()
{
RouteTable.Routes.MapConnection<MyConnection>("echo", "echo/{*operation}");
這樣服務器端就完成了。現在我們在項目中Master、View (Home/Index),然后加入必要的代碼:
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.6.4.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.signalR-0.5.2.min.js")" type="text/javascript"></script>
</head>
@{
ViewBag.Title = "Home Page";
}
<script type="text/javascript">
$(function () {
var connection = $.connection('echo');
connection.received(function (data) {
$('#messages').append('<li>' + data + '</li>');
});
connection.start();
$("#broadcast").click(function () {
connection.send($('#msg').val());
});
$("#btnStop").click(function () {
connection.stop();
});
});
</script>
<h2>@ViewBag.Message</h2>
<input type="text" id="msg" />
<input type="button" id="broadcast" value="發送" />
<input type="button" id="btnStop" value="停止" />
<ul id="messages">
</ul>
運行起來就是這個效果:
下面我們來展示 SignalR 的另一個功能:由服務器端調用客戶端的 JavaScript 腳本的功能,而這個功能的要求必須是要實現成 Hub 的模式,因此我們可以順便看到如何實現一個 Hub 類型的 SignalR 應用程序。
向項目中加入一個類Chat繼承自 Hub 類 (這是 Hub 應用程序的要求) :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using SignalR.Hubs;
using System.Threading.Tasks;
using System.Threading;
namespace MvcApplicationSignalR
{
[HubName("geffChat")]
public class Chat : Hub
{
public void SendMessage(string message)
{
Clients.sendMessage(message);
}
}
}
這段程序代碼的用意是,在連接進到 Hub 時,將連接代碼加到聯機用戶的集合中,等會就會使用到,因為我們會依照客戶端的 ID 來調用客戶端腳本。
1. HubName:這個 atttibute 代表 client 端要如何建立對應 server 端對象的 proxy object。通過 HubName , server 端的 class name才不會被 client 綁死。如果沒有設定,則會以 server 端 class name 為 HubName 默認值。
2. 繼承 Hub:繼承 Hub 之后,很多對應的設計就都不用寫了,我們只需要把注意力放在 client 如何送 request 給 server的 hub , server 如何通知 client 即可。
3. public void SendMessage(string message) ,就像 WebService Method 或 PageMethod 一般, client 端通過 proxy object ,可以直接調用 server 端這個方法。后續會介紹到如何在頁面上使用。
4. Clients 屬性:代表所有有使用 Chat 的頁面。而 Clients 的型別是 dynamic ,因為要直接對應到 JavaScript 的對象。
5. Clients.sendMessage(message):代表 server 端調用 Clients 上的 sendMessage 方法,也就是 JavaScript 的方法。
6. 總結: Chat 對象職責就是當 client 端調用SendMessage() 方法后,要把這個 message ,送給所有 client 頁面上呈現。以達到聊天室的功能。
服務端的做完了,開始制作客戶端,同樣在Home/Index頁面上增加以下html代碼
<%--很重要的一個參考,一定要加,且在這一行之前,一定要先參考jQuery.js與signalR.js--%>
<script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script>
@{
ViewBag.Title = "Home Page";
}
<script type="text/javascript">
$(function () {
var connection = $.connection('/echo');
connection.received(function (data) {
$('#messages').append('<li>' + data + '</li>');
});
connection.start();
$("#broadcast").click(function () {
connection.send($('#msg').val());
});
$("#btnStop").click(function () {
connection.stop();
});
// 建立對應server端Hub class的對象,請注意geffChat的第一個字母要改成小寫
var chat = $.connection.geffChat;
// 定義client端的javascript function,供server端hub,通過dynamic的方式,調用所有Clients的javascript function
chat.sendMessage = function (message) {
//當server端調用sendMessage時,將server push的message數據,呈現在wholeMessage中
$('#wholeMessages').append('<li>' + message + '</li>');
};
$("#send").click(function () {
//調用叫server端的Hub對象,將#message數據傳給server
chat.sendMessage($('#message').val());
$('#message').val("");
});
//把connection打開
$.connection.hub.start();
});
</script>
<h2>@ViewBag.Message</h2>
<input type="text" id="msg" />
<input type="button" id="broadcast" value="發送" />
<input type="button" id="btnStop" value="停止" />
<ul id="messages">
</ul>
<div>
<input type="text" id="message" />
<input type="button" id="send" value="發送" />
<div>
聊天室內容:
<br />
<ul id="wholeMessages">
</ul>
</div>
</div>
1. 先引用 jQuery 與 signalR 的 js 文件。
2. 很重要的一個步驟:加入一個 js 引用,其路徑為「根目錄/signalr/hubs」。 SignalR 會建立相關的 JavaScript,放置於此。
3. 通過 $.connection.『server 端的 HubName』,即可建立對應該 hub 的 proxy object。要注意,首字母需小寫。
4. 定義 client 端上,供 server 端通知的 JavaScript function,這邊的例子是 sendMessage。
5. 當按下發送按鈕時,調用 server 端的 SendMessage() 方法,只需要直接通過 proxy object 即可。要注意,首字母需小寫。
6. 記得透過 $.connection.hub.start() ,把 connection 打開。
注意:SingalR 會自動生成一個siganlr/hub 的橋接js..,在本機使用localhost測試都不會有問題。當部署到IIS的時候會發生404錯誤,是由於被IIS誤判可能是虛擬目錄…,解決方法是在web.config加入一段:
<!-- 加入下面這一段-->
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
</modules>
</system.webServer>
參考:
- https://github.com/SignalR/SignalR/wiki/QuickStart-Persistent-Connections
- https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs
- SignalR - Group Notifications
- http://www.thinqlinq.com/Post.aspx/Title/SignalR-and-Reactive-Extensions-are-an-Rx-for-server-push-notifications
- Event Broker using Rx and SignalR (Part 3: Event Consumers)
- Part 1: A Fluent API
Part 2: Implementation
Part 4: Solving the Scenario - 使用HTML5+Singalr搭建多機協同畫板(一)
- 用SignalR創建實時永久長連接異步網絡應用程序
- SignalR – Introduction to SignalR – Quick Chat App
- SignalR – Push Data To Clients Using IHubContext
- SignalR - Publish Data From Win Forms Using Hub Proxies
- SignalR-Dependency Injection
- Scaling SignalR with Redis
- Asynchronous scalable web applications with real-time persistent long-running connections with SignalR
- ASP.NET MVC, SignalR and Knockout based Real time UI syncing - For Co Working UIs and Continuous Clients
- Sending Notifications using ASP.NET SignalR