在前一個範例中,我們己經實作出來一個簡單的應用程式,而這次我們要來展示 SignalR 的另一個功能:由伺服端呼叫用戶端的 JavaScript 指令碼的功能,而這個功能的要求必須是要實作成 Hub 的模式,因此我們可以順便看到如何實作 Hub 類型的 SignalR 應用程式。
在前一個範例中,我們己經實作出來一個簡單的應用程式,而這次我們要來展示 SignalR 的另一個功能:由伺服端呼叫用戶端的 JavaScript 指令碼的功能,而這個功能的要求必須是要實作成 Hub 的模式,因此我們可以順便看到如何實作 Hub 類型的 SignalR 應用程式。
一樣的,我們在專案內加入一個新的類別 PushNotification,並且設定繼承 Hub 類別 (這是 Hub 應用程式的要求) 以及實作 IConnected, IDisconnect 兩個介面:
using System.Threading;
using System.Threading.Tasks;
using SignalR;
using SignalR.Hubs;
using SignalR.Infrastructure;
using SignalR.Hosting.AspNet;
namespace ServerPushMessageApplication
{
[HubName("push")]
public class PushNotification : Hub, IConnected, IDisconnect
{
// ...
}
}
接著,加入連線保留的物件以及實作 IConnected/IDisconnect 方法:
private static List<string> _connectionIds = new List<string>();
private static Task pushTask = null;
public Task Disconnect()
{
if (_connectionIds.Contains(this.Context.ConnectionId))
_connectionIds.Remove(this.Context.ConnectionId);
return null;
}
public Task Connect()
{
_connectionIds.Add(this.Context.ConnectionId);
return null;
}
public Task Reconnect(IEnumerable<string> groups)
{
return null;
}
這段程式碼的用意是,在連線進到 Hub 時,將連線代碼加到連線用戶的集合中,等會就會使用到,因為我們會依照用戶端的 ID 來呼叫用戶端指令碼。
接著,我們加入真正重要的程式碼,也就是 Push 用的程式碼:
public PushNotification()
{
pushTask = new Task(() =>
{
while (true)
{
if (_connectionIds.Count > 0)
{
foreach (string id in _connectionIds)
Clients[id].updateDateTime(DateTime.Now.ToString());
}
Thread.Sleep(10000);
}
});
pushTask.Start();
}
~PushNotification()
{
pushTask = null;
}
這段程式碼的用意是,會在每十秒鐘時呼叫用戶端的 updateDateTime() 指令碼,並且將參數傳回用戶端,參數的排列與伺服端傳入的順序相同,等一下我們會實作到用戶端的 JavaScript,屆時就可以比較一下。
接著,我們在 Global.asax 的程式中,加入啟用 Hub 的程式碼:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// enable Hub
RouteTable.Routes.MapHubs();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
}
至此,伺服器端就寫好了,看起來不難吧。
接著,在專案中加入新的 View,然後加入下列的 HTML/JavaScript:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src="http://code.jquery.com/jquery-1.7.min.js" type="text/javascript"></script> <script src="Scripts/jquery.signalR-0.5.2.min.js" type="text/javascript"></script> <script src="/signalr/hubs" type="text/javascript"></script> <script type="text/javascript"> $(function () { var push = $.connection.push; push.updateDateTime = function (d) { $("#messages").append($("<li />").text(d)); }; $.connection.hub.start(); }); </script> </head> <body> <ul id="messages"> </ul> </body> </html>
用戶端部份和 Persistent Connection 的不同,就是在用戶端必須要加入 “/singler/hubs” 這個指令碼來源,它會產生 hub 的 metadata,如果沒有它的話,用戶端針對 hub 的指令碼 ($.connection.push) 會失效。
其中比較重要的是 push.updateDateTime 的實作,它就是要負責接收來自伺服端的資料並在用戶端做處理的,但它必須要掛在 hub 物件之下,否則沒辦法被呼叫,這也是為什麼 SignalR 能做伺服端呼叫用戶端的原因。
完成後,執行結果如下:
可以參考用戶端程式碼,可發現用戶端只有啟動連線而己,並沒有主動呼叫伺服器端,但伺服器端卻可以傳回資料到用戶端,這就是 SignalR 的神奇之處 (等以後我們討論到細節時,你就不會覺得它神奇了 :) )。
Reference: https://github.com/SignalR/SignalR/wiki/QuickStart-Hubs