SignalR - .Net Real Time Web傳輸技術

先前限量提到了WebSocket這個HTML5標準的網頁即時傳輸技術,如果是用.Net的開發者有福了,因為Microsoft為了ASP.Net也能簡單使用即時傳輸技術,開啟了SignalR Project。

簡單來說,SignalR是個提供開發人員能夠輕鬆使用更Hight Level API網頁即時傳輸技術的Library,SignalR將過去與現在實作即時傳輸的方法整合在一起,根據不同狀況選擇適合的方法,WebSocket就是其中之一,且WebSocket為SignalR預設選擇的方法。像大部分的情況下,大家都會想使用WebSocket,畢竟這是目前最佳的解決方法,但要使用WebSocket不僅Client瀏覽器要支援WebSocket之外,Server也限制只能在IIS 8以上才有支援(IIS部屬WebSocket服務)。這時候如果使用Signal來實作的話就可以不用花時間考慮這麻煩的問題,因為SignalR會自動選擇用WebSocket或Long Polling的方法,下圖為SignalR選擇傳輸方式的流程圖。



要使用SignalR很簡單,只要在你的Web專案裡有一個Hub的Class,並在Hub裡實作Server端要進行的操作即可,接著在Client端要有對應的JavaScript檔與Hub進行溝通,就輕鬆完成即時傳輸的網頁。下面限量將在MVC5專案中使用SignalR完成簡單的聊天室功能:

(1) 在NuGet裡搜尋SignalR並安裝(目前版本來到2.0)。


加入之後可以看到Script裡多了一些JS檔。

(2) 在專案裡增加一個OWIN Startup Class。(注意:如果在建立MVC5專案時如果有選擇一些設定時,MVC5會幫你建立一個Startup的Class,一個專案只有一個Startup Class,所以要注意看看專案裡是否本來就有存在了)。



(3) 將Startup.cs修改成以下程式碼(注意:如果專案本身就有Startup Class時,在安裝好SignalR時就會自動幫你加入[assembly: OwinStartup( typeof( WebSample.Startup ) )],所以只要在Configuration()裡加上app.MapSignalR()。還有另一點要注意的是,下面修改的程式碼是Signal 2.0的修改方式,不適用於2.0之前的版本)。
Startup.cs


using Microsoft.Owin;
using Owin;

[assembly: OwinStartup( typeof( WebSample.Startup ) )]

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

}


(4) 在專案裡加入Hub Class,並撰寫Server端邏輯處理的方法(注意:由於安裝SignalR後不會建立額外的資料夾,所以SignarR相關的程式碼可根據自己的規劃放置)。



ChatHub.cs
using Microsoft.AspNet.SignalR;

namespace WebSample
{
 public class ChatHub : Hub
 {

  public void Echo( string name, string msg )
  {
   Clients.All.sendMsgToOthers( name, msg );
  }
  
  public void Hello(string name)
  {
   Clients.All.welcome(string.Format("{0} 加入聊天", name));
  }
 }
}

在範例中,限量撰寫了兩個方法,Hello為當有遊客進入聊天室時,Server會取得該遊客的名字並通知其他遊客該遊客加入聊天;Echo為當遊客發送訊息出來時,Server會將訊息導至其他遊客的視窗中。

(5) 在View裡加入與Hub溝通的JavaScript Code。

Index.cshtml


<h2>聊天室</h2>

<div>
    <ul id="msg_list"></ul>
</div>
<div>
    @Html.TextBox( "msg", null, new { @class = "form-control", placeholder = "在此輸入訊息" } )
    <input type="button" class="btn" value="送出" id="btn_send" />
    @Html.Hidden("user_name")
</div>

@section scripts
{
    <script src="~/Scripts/jquery.signalR-2.0.3.js"></script>
    <script src="~/signalr/hubs"></script>
    <script>
        $(function () {
            var chat = $.connection.chatHub;
            $('#user_name').val(prompt('輸入暱稱:', ''));

            chat.client.welcome = function (msg) {
                alert(msg);
            };

            chat.client.sendMsgToOthers = function (name, msg) {
                $('#msg_list').append('<li>'+name+': '+msg+'</li>');
            };

            $.connection.hub.start().done(function () {
                chat.server.hello($('#user_name').val());
            });

            $('#btn_send').click(function () {
                chat.server.echo($('#user_name').val(), $('#msg').val());
            });
        });
    </script>
}

在撰寫對應的JavaScript Code時,要將jquery.signalR-2.0.3.js拉進來,並引用~/signalr/hubs,你一定會覺得很奇怪,在Script資料夾裡根本沒有hubs這鬼檔案。沒錯,那就是鬼檔案,因為這個js檔只會在執行的時候出現,是SignalR Library自動產生來與Hub作對應。



在JS Code裡面,首先要宣告一個Hub的對應物件才能進行雙向溝通的操作,使用$.connection.{Hub Class名稱}取得物件。接著在依序實作在Server中呼叫的對應JS方法,這些方法必須放在{Hub物件}.client的Namespace下。在對應ChatHub.cs與JS Code可以看到兩者之間的關係。

再來在$.connection.hub.start().done(function(){...})裡實作與Server連線啟動時要做的事,就和WebSocket的onopen()監聽事件一樣。


執行結果:

從執行結果可以看到和WebSocket一模一樣的效果,只要少許的程式碼也不用另外加一個WebSocket Server就能夠實現即時傳輸的網頁,真是太方便了。



參考來源:

留言