[食譜好菜] 使用 ASP.NET SignalR 實現主動式即時廣播通知

ASP.NET SignalR 技術出來好幾年了,一直沒有為它發個文,網路上也有很多相關的文章,大家 Google 一下就很多了,我參考了黑大的文章,而黑大的文章裡面也有連結到其他兩位專家的文章,大家可以去看看,我這邊僅記錄自己實作過程及心得。

ASP.NET SignalR 基本運作概念

SignalR 是集中式的架構,訊息的傳遞由伺服器提供的集中器(Hub)統一處理,在 Hub 中需要宣告給客戶端呼叫的方法,而客戶端需要至少指定一個 Hub 並為指定的 Hub 建立 Proxy,而且也要宣告給伺服器端呼叫的方法,完成之後進行連線,兩邊就透過呼叫彼此的方法進行溝通。

客戶端與客戶端之間彼此要溝通,需要指定同一個 Hub,將訊息透過 Hub 提供的方法傳送到 Hub 上,由 Hub 決定要將訊息往外傳遞給哪些客戶端,客戶端之間借助這樣的路由機制就可以溝通了。

放在伺服器端 Hub 上給客戶端呼叫的方法及放在客戶端 Hub Proxy 上給伺服器端呼叫的方法,方法名稱不一定要一樣,只要呼叫的時候方法名稱不要打錯、參數的數量要給對就可以了。

架設 SignalR 伺服器

既然是 ASP.NET SignalR 當然我們就要建立一個 ASP.NET 的專案,我這邊是用 ASP.NET Web Application (.NET Framework) 這個專案範本建立一個 ASP.NET MVC 的空白專案。

為了方便測試,我建立了一個發送訊息的介面,有 Name 及 Message 欄位。

接著我們到 NuGet 安裝 Microsoft.AspNet.SignalR 套件。

Microsoft.AspNet.SignalR 安裝好之後,我們要先建立一個 Hub,名字就叫 BroadcastHub,裡面就宣告一個給客戶端呼叫的方法 Broadcast(string name, string message),方法裡面我要去呼叫所有客戶端的 showmessage 方法,參數是 namemessage

public class BroadcastHub : Hub
{
    public void Broadcast(string name, string message)
    {
        Clients.All.showmessage(name, message);
    }
}

最後建立一個 Startup 把它註冊在 OwinStartup,伺服器的部分大致上就準備完成了。

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(AspNetSignalRSamples.SignalR.Startup))]

namespace AspNetSignalRSamples.SignalR
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

實作 SignalR Web 客戶端

關於 SignalR 客戶端的部分,如果我們可以找到相對應的 SignalR Client Api,我們就可以來操作 SignalR 技術,我這邊只介紹 Web 跟 Windows,其他的就留待大家自行發掘。

SignalR 的 Web Client 我們可以引入 jquery.signalR-x.x.x.js 這個 js 檔來實現,引入之後首要第一件事就是建立 Hub Proxy,客戶端建立 Hub Proxy 有兩種方法。

With the generated proxy

SignalR 的伺服器端 Web Site 預設會提供一個 /signalr/hubs Web Api,裡面其實是一堆指令碼,它是由 SignalR 自己自動產生的 Hub Proxy Script,只要引用它就有 Hub Proxy 可以用,不用自己辛苦建立。

<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script src="~/signalr/hubs"></script>
<script>
    $(function () {
        // Create Hub Proxy
        var broadcastHub = $.connection.broadcastHub;

        // ToDo: 註冊給伺服器端呼叫的方法

        $.connection.hub.start()
            .done(function () {
                // ToDo: 與伺服器連線註冊成功後要執行的作業
            });
   });
</script>

Without the generated proxy

如果我們不引用 Hub Proxy Script,就要自己寫 Code 建立,其實也沒有多打多少程式碼。

<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script>
    $(function () {
        // Create Hub Proxy
        var connection = $.hubConnection();
        var broadcastHub = connection.createHubProxy('broadcastHub');

        // ToDo: 註冊給伺服器端呼叫的方法

        connection.start()
            .done(function () {
                // ToDo: 與伺服器連線註冊成功後要執行的作業
            });
   });
</script>

何時該用 Without the generated proxy 的方式?

差別在於如果你要用同一個客戶端方法,執行不同的事件處理,就必須得用 Without the generated proxy 的方式。

舉例來講,我在客戶端宣告兩個給伺服器端呼叫的 hello 多載方法,一個要把訊息顯示在網頁上,一個要跳出 alert 視窗,兩個 hello 方法的參數個數也不一樣,如果用 With the generated proxy 的方式,就只會認得最後註冊的那一個,前面不管註冊多少多載方法都沒有用。

<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script>
    $(function () {
        // ToDo: Create Hub Proxy

        broadcastHub.on('hello', function (name) {
            $('#messages').append('<li><strong>' + htmlEncode(name) + '</strong>: ' + htmlEncode(message) + '</li>');
        });

        broadcastHub.on('hello', function (name, age) {
            alert(name + ', ' + age);
        });

        // ToDo: Start Connection
   });
</script>

而對於伺服器來講只要呼叫最多參數的那個 hello 方法就可以了。

public class HelloHub : Hub
{
    public void Hello()
    {
        Clients.All.hello("Johnny", 28);
    }
}

不過要自己寫 Code 產生 Hub Proxy 的情境比較少,所以我們預設還是優先選擇引用 SignalR 幫我們產生的 Hub Proxy Script,無法滿足的時候再考慮用另外一個方法。

建立完 Hub Proxy 之後,接著註冊給伺服端呼叫的方法、連線到 SignalR 伺服器、註冊連線成功後要執行的作業,就完成了。

<script src="~/Scripts/jquery.signalR-2.2.0.js"></script>
<script src="~/signalr/hubs"></script>
<script>
    $(function () {
        // With the generated proxy
        // Create Hub Proxy
        var broadcastHub = $.connection.broadcastHub;

        // 註冊給伺服端呼叫的方法
        broadcastHub.client.showmessage = function (name, message) {
            $('#messages').append('<li><strong>' + htmlEncode(name) + '</strong>: ' + htmlEncode(message) + '</li>');
        };

        // 連線到 SignalR 伺服器
        $.connection.hub.start()
            .done(function () {
                // 註冊連線成功後要執行的作業
                $('#sendmessage').click(function () {
                    broadcastHub.server.broadcast($('#name').val(), $('#message').val());
                });
            });
    });

    function htmlEncode(value) {
        var encodedValue = $('<div />').text(value).html();
        return encodedValue;
    }
</script>

實作 SignalR Windows 客戶端

Windows 客戶端的部分我們可以仰賴 Microsoft.AspNet.SignalR.Client 套件,安裝好之後做法跟 Web Client 是差不多的,建立 Hub Proxy、註冊給伺服端呼叫的方法、連線到 SignalR 伺服器。

各位觀眾!!!…三方會談!!!

參考資料

 < Source Code >