文章目錄
- 一,簡介
- 二,實現機制
- 三,Hub 樣本教程
- 四,Persistent Connection 樣本教程
在 Asp.NET MVC 中使用 SignalR 實現推送功能
羅朝輝 ( http://www.cnblogs.com/kesalin/ )
CC許可,轉載請註明出處 一,簡介
Signal 是微軟支援的一個運行在 Dot NET 平台上的 html websocket 架構。它出現的主要目的是實現伺服器主動推送(Push)訊息到用戶端頁面,這樣用戶端就不必重新發送請求或使用輪詢技術來擷取訊息。
可訪問其官方網站:https://github.com/SignalR/ 擷取更多資訊。
二,實現機制
SignalR 的實現機制與 .NET WCF 或 Remoting 是相似的,都是使用遠程代理來實現。在具體使用上,有兩種不同目的的介面:PersistentConnection 和 Hubs,其中 PersistentConnection 是實現了長時間的 Javascript 輪詢(類似於 Comet),Hub 是用來解決即時資訊交換問題,它是利用 Javascript 動態載入執行方法實現的。SignalR 將整個串連,資訊交換過程封裝得非常漂亮,用戶端與伺服器端全部使用 JSON 來交換資料。
下面就 Hubs 介面的使用來講講整個流程:
1,在伺服器端定義對應的 hub class;
2,在用戶端定義 hub class 所對應的 proxy 類;
3,在用戶端與伺服器端建立串連(connection);
4,然後用戶端就可以調用 proxy 對象的方法來調用伺服器端的方法,也就是發送 request 給伺服器端;
5,伺服器端接收到 request 之後,可以針對某個/組用戶端或所有用戶端(廣播)發送訊息。
三,Hub 樣本教程1,工具準備
SignalR 運行在 .NET 4.5 平台上,所以需要安裝 .NET 4.5。為了方便示範,本樣本使用 ASP.NET MVC 在 Win 7 系統來實現。這需要安裝 ASP.NET MVC 3 或 ASP.NET MVC 4。
2,建立工程
開啟 VS2010/VS2012 建立名為 SignalRTutorial 的 ASP.NET MVC 3 Web Application 工程,選擇 Internet Application 模板, Razor 視圖引擎以及勾選 Use HTMl 5 標籤。
3,安裝 SignalR
開啟 NuGet 的 package manager console(Tools->Library package manager),輸入:install-package SignalR.Sample,斷行符號安裝。:
4,實現 Hub 伺服器端代碼
向工程中建立 SignalR 目錄,在其中添加 ChatHub.cs 檔案,內容如下:
namespace SignalTutorial.SignalR{ [HubName("chat")] public class Chat : Hub { public void Send(string clientName, string message) { //var toSelfinfo = "You had sent message " + message; //Caller.addSomeMessage(clientName, toSelfinfo); // Call the addMessage method on all clients Clients.addSomeMessage(clientName, message); //Clients[Context.ConnectionId].addSomeMessage(clientName, data); } }}
在上面的代碼中:
1),HubName 這個特性是為了讓用戶端知道如何建立與伺服器端對應服務的代理對象,如果沒有設定該屬性,則以伺服器端的服務類名字作為 HubName 的預設值;
2),Chat 繼承自 Hub,從下面 Hub 的介面圖可以看出:Hub 支援向發起要求者(Caller),所有用戶端(Clients),特定組(Group) 推送訊息。
3),public void Send(string clientName, string message) 這個介面是被用戶端通過代理對象調用的;
4),Clients 是 Hub 的屬性,表示所有連結的用戶端頁面,它和 Caller 一樣是 dynamic,因為要直接對應到 Javascript 對象;
5),Clients.addSomeMessage(clientName, message); 表示伺服器端調用用戶端的 addSomeMessage 方法,這是一個 Javascript 方法,從而給用戶端推送訊息。
6),總結:這裡實現的服務很簡單,就是當一個用戶端調用 Send 方法向伺服器發送 message 後,伺服器端負責將該 message 廣播給所有的用戶端(也可以給特定組或特定用戶端,見屏蔽代碼),以實現聊天室的功能。
5,實現 Hub 用戶端代碼1),引用 SignalR Javascript
為了簡化引用 SignalR 指令碼操作,我直接在 View/Shared/_Layout.cshtml 中引入 SignalR 及其他指令碼:
<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.5.1.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery-1.6.4.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery-ui-1.8.24.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/jquery.signalR-0.5.3.js")" type="text/javascript"></script> <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script> <script src="@Url.Content("~/signalr/hubs")" type="text/javascript"></script> </head>
注意:signalR 依賴於 jquery,所以 signalR 必須放在 jquery 之後,而 hubs 又必須放在 signalR 之後。
然後在 body 部分加入 HubChat Tab:
<li>@Html.ActionLink("HubChat", "HubChat", "Home")</li>
2),產生訪問頁面
在 HomeController 中添加如下方法:
public ActionResult HubChat(){ ViewBag.ClientName = "使用者-" + Rnd.Next(10000, 99999); return View();}
這裡由伺服器根據隨機數來設定用戶端的名字,不夠嚴謹,因為隨機數產生的名字不是唯一的的,在這裡僅為簡化示範,實際應用中應該使用 GUID 。
然後產生對應的 View:HubChat.cshtml
@model dynamic@{ ViewBag.Title = "title";}<script src="@Url.Content("~/Scripts/hubDemo.js")" type="text/javascript"></script><script type="text/javascript"> $(document).ready(function () { });</script><h2>Hub Chat</h2><div> <input type="text" id="Placeholder" value="@ViewBag.ClientName" hidden="true"/> <input type="text" id="msg" /> <input type="button" id="broadcast" value="廣播" /> <br /> <br /> <h3> 訊息記錄: (你是:<span id="MyClientName">@ViewBag.ClientName</span>): </h3> <ul id="messages"> </ul></div>
在上面的頁面代碼中,我添加了名為 hubDemo.js 的指令碼,這將在下面介紹;此外還有一個id 為 Placeholder 的隱藏 input 控制項,這是為了向 Javascript 中傳遞用戶端的名字。
3),編寫 Javascript
向 Scripts 目錄添加新的 Javescript 指令碼:hubDemo.js。其內容如下:
$(function () { var myClientName = $('#Placeholder').val(); // Proxy created on the fly var chat = $.connection.chat; // Declare a function on the chat hub so the server can invoke it chat.addSomeMessage = function (clientName, message) { writeEvent('<b>' + clientName + '</b> 對大家說: ' + message, 'event-message'); }; $("#broadcast").click(function () { // Call the chat method on the server chat.send(myClientName, $('#msg').val()) .done(function () { console.log('Sent message success!'); }) .fail(function (e) { console.warn(e); }); }); // Start the connection $.connection.hub.start(); //A function to write events to the page function writeEvent(eventLog, logClass) { var now = new Date(); var nowStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); $('#messages').prepend('<li class=" + logClass + "><b>' + nowStr + '</b> ' + eventLog + '.</li>'); }});
上面代碼有詳細的注釋,下面再講講關鍵之處:
1,首先擷取用戶端頁面的名字;
2,然後通過 $.connection.chat 建立對應伺服器端 Hub 類的代理對象 chat;
3,定義用戶端的 Javascript 方法 addSomeMessage ,伺服器通過 dynamic 方式調用用戶端的該方法以實現推送功能。在這裡每當收到伺服器推送來的訊息,就在用戶端頁面的 messages 列表表頭插入該訊息。
4,當點擊廣播按鈕時,用戶端通過代理對象調用伺服器端的 send 方法以實現向伺服器發送訊息。
5,通過 $.connection.hub.start(); 語句開啟連結。
6),編譯運行 Hub 樣本
在多個瀏覽器視窗開啟頁面,效果如下:
四,Persistent Connection 樣本教程1,實現伺服器端代碼1),編寫伺服器 PersistentConnection 代碼
向工程中 SignalR 目錄中添加 PersistentConnection.cs 檔案,內容如下:
using System;using System.Collections.Generic;using System.Threading.Tasks;using SignalR;namespace SignalTutorial.SignalR{ public class MyConnection : PersistentConnection { protected override Task OnConnectedAsync(IRequest request, string connectionId) { return Connection.Broadcast("Connection " + connectionId + " connected"); } protected override Task OnReconnectedAsync(IRequest request, IEnumerable<string> groups, string clientId) { return Connection.Broadcast("Client " + clientId + " re-connected"); } protected override Task OnReceivedAsync(IRequest request, string connectionId, string data) { var info = data + ". ConnectionId is [" + connectionId + "]"; // return Connection.Send(connectionId, info); // Broadcast data to all clients return Connection.Broadcast(info); } protected override Task OnDisconnectAsync(string connectionId) { return Connection.Broadcast("Connection " + connectionId + " disconncted"); } protected override Task OnErrorAsync(Exception error) { return Connection.Broadcast("Error ocurred " + error); } }}
在上面的代碼中:
1,MyConnection 繼承自 PersistentConnection,這樣我們就能在用戶端串連,重串連,中斷連線,發送訊息以及串連出錯的情況下進行相關的處理。從下面的 PersistentConnection 介面中可以看到,PersistentConnection 同樣支援組進行推送。
2,推送訊息由 PersistentConnection 的屬性 Connection 來提供,它繼承自 IConnection 介面,該介面提供兩個函數來實現對特定用戶端的推送和廣播功能。
System.Threading.Tasks.Task Send(string signal, object value)
System.Threading.Tasks.Task Broadcast(object value)
2),配置訪問路由
為了支援用戶端訪問,需要在路由表中進行配置。開啟 Global.asax.cs ,修改 Application_Start() 函數如下:
protected void Application_Start(){ AreaRegistration.RegisterAllAreas(); RouteTable.Routes.MapConnection<MyConnection>("echo", "echo/{*operation}"); RegisterGlobalFilters(GlobalFilters.Filters); RegisterRoutes(RouteTable.Routes); // Make connections wait 50s maximum for any response. After // 50s are up, trigger a timeout command and make the client reconnect. GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(50); //DisconnectTimeout //HeartBeatInterval //KeepAlive }
在上面的代碼中,我將 echo 及其子路徑的訪問映射到 MyConnection 上,並設定連線逾時時間為 50 s。在這裡還可以設定其他的一些參數,如斷連逾時時間,心跳間隔等。
2,實現用戶端代碼1),產生訪問頁面
在前面三 Hub 樣本教程的基礎上,我們向該工程加入使用 Persistent Connection 的示範。和前面一樣,向 _Layout.cshtml 中加入 PersistentChat Tab:
<li>@Html.ActionLink("PersistentChat", "PersistentChat", "Home")</li>
然後在 HomeController 中添加如下方法:
public ActionResult PersistentChat(){ ViewBag.ClientName = "使用者-" + Rnd.Next(10000, 99999); return View();}
這裡由伺服器根據隨機數來設定用戶端的名字,不夠嚴謹,因為隨機數產生的名字不是唯一的的,在這裡僅為簡化示範,實際應用中應該使用 GUID 。
然後產生對應的 頁面: PersistentChat.cshtml:
@model dynamic@{ ViewBag.Title = "title";}<script src="@Url.Content("~/Scripts/persistent.js")" type="text/javascript"></script><h2>Persistent Chat</h2><div> <input type="text" id="Placeholder" value="@ViewBag.ClientName" hidden="true"/> <input type="text" id="msg" /> <input type="button" id="broadcast" value="廣播" /> <br /> <br /> <h3> 訊息記錄: (你是:<span id="MyClientName">@ViewBag.ClientName</span>): </h3> <ul id="messages"> </ul></div>
在上面的頁面代碼中,我添加了名為 persistent.js 的指令碼,這將在下面介紹;此外還有一個id 為 Placeholder 的隱藏 input 控制項,這是為了向 Javascript 中傳遞用戶端的名字。
2),編寫 Javascript
向 Scripts 目錄添加新的 Javescript 指令碼:persistent.js。其內容如下:
$(function () { var myClientName = $('#Placeholder').val(); var connection = $.connection('/echo'); connection.received(function (data) { var msg = new String(data); var index = msg.indexOf("#"); var clientName = msg.substring(0, index); var content = msg.substring(index + 1); if (clientName == null || clientName == "") { writeEvent('<b>' + "系統訊息" + '</b>: ' + content, 'event-message'); } else { writeEvent('<b>' + clientName + '</b> 對大家說: ' + content, 'event-message'); } }); connection.start(); $("#broadcast").click(function () { var msg = myClientName + "#" + $('#msg').val(); connection.send(msg); }); //A function to write events to the page function writeEvent(eventLog, logClass) { var now = new Date(); var nowStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds(); $('#messages').prepend('<li class=" + logClass + "><b>' + nowStr + '</b> ' + eventLog + '.</li>'); }});
上面的代碼基本與前面的 Hub 實現相同,在此就不再一一講述。有兩點值得說明:
1,建立串連時,指定路徑為 "/echo",該路徑在伺服器端的路由映射表被映射為 MyConnection,因而這個串連就被指向前面提供的 MyConnection。
2,將 clientName 資訊放入 message 中,並用 # 將 clientName 和訊息內容串連成一個 msg。
3,編譯運行 Persistent 樣本
五,引用
SignalR:https://github.com/SignalR/
利用SignalR實現遠端程式遙控功能:
http://blog.darkthread.net/post-2012-07-10-signalr-remote-controller.aspx
一個很酷的同步動作表格的樣本(使用 jTable ):
http://www.codeproject.com/Articles/315938/Real-time-Asynchronous-Web-Pages-using-jTable-Sign
組通知樣本:
http://www.codeproject.com/Articles/404662/SignalR-Group-Notifications