[.NET] signalR 2.2 & VS2012 WebForm 簡易開發教學

  • 3252
  • 0
  • 2018-09-07

摘要:[.NET] signalR 2.2 & VS2012 簡易開發教學

前言:

因工作上的需求,要實現一個可以即時對所有正在使用網頁的User發布訊息的廣播系統。

所以用signalR來實作此一功能,參考了很多網站,大部分都還是舊版的安裝方式並不太適合目前版本。

 

signalR簡介:

SignalR 的任務是提供開者者一套非常易於使用的高階 API,用來實現伺服器端與瀏覽器間的遠程程序呼叫 (RPC,Remote Procedure Call),在這個架構下伺服器端以 .NET 開發,瀏覽器端則主要是以 JavaScript 開發,SignalR 提供了優異的連線/斷線管理以及擴充模型、連線/斷線的事件通知,在訊息溝通的本質上則提供了令人期待的內建群組連線、還有相關的身份驗證檢查機制 。
 
SignalR 會針對目前執行的瀏覽器進行判斷,找到與伺服器間最適合的建立連線方式,SignalR 會優先的選用 WebSocket 技術與伺服器溝通,當然伺服器上的通訊協定管理方式也內含在 ASP.NET SignalR 的組件庫中,開發人員就不需要針對目前走的是 Web Socket、Polling 還是 Long Polling 進行特別的處理,所有的 code 都透過 ASP.NET SignalR高階的 API 。

安裝:
首先從參考->右鍵->管理NuGet套件

 
搜尋signalR->安裝
 
其餘相關套件會一併安裝,並自動加入參考和javascript。
 
開發&設定
先建立Owin類別檔Startup.cs 內容為:

using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;

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

namespace SignalRChat
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // 如需如何設定應用程式的詳細資訊,請參閱  http://go.microsoft.com/fwlink/?LinkID=316888

            //*********************************************************
            app.MapSignalR();   //自己動手新增這一段,這是 SignalR v2版需要的。
            //*********************************************************
        }
    }
}
接下來再新增一個 signalR Hub類別檔

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
using System.Threading.Tasks;
using Microsoft.Owin;


public class signalRChat : Hub
{
    // 呼叫所有使用者
    //Clients.All.alertMessage(message);
    // 呼叫除了自己的使用者
    //Clients.Others.alertMessage(message);
    // 呼叫所有使用者除了某個 ConnectionId 的使用者
    //Clients.AllExcept(Context.ConnectionId).alertMessage(message);
    // 呼叫自己
    //Clients.Caller.alertMessage(message);
    // 呼叫特定使用者
    //Clients.Client(Context.ConnectionId).alertMessage(message);
    // 呼叫群組使用者
    //Clients.Group(groupId).alertMessage(message);
    
    //宣告靜態類別,來儲存上線清單
    public static class UserHandler
    {
        public static Dictionary ConnectedIds = new Dictionary();
    }
    //使用者連線時呼叫
    public void userConnected(string name, string groupId)
    {
        //進行編碼,防止XSS攻擊
        name = HttpUtility.HtmlEncode(name);

        //新增目前使用者至上線清單
        UserHandler.ConnectedIds.Add(Context.ConnectionId, name);
       //新增使用者至群組
        Groups.Add(Context.ConnectionId, groupId);
    }

    //發送訊息給所有人
    public void sendAllMessage(string message)
    {
        message = HttpUtility.HtmlEncode(message);
        Clients.All.alertMessage(message); //alertMessage 為client端 function名稱
    }

    //發送訊息至特定使用者
    public void sendMessage(string connectId, string message)
    {
        message = HttpUtility.HtmlEncode(message);
        Clients.Clients(connectId).alertMessage(message); //alertMessage 為client端 function名稱
        
    }
    
    //發送訊息至特定使用者名稱
    public void sendMessageByName(string userName, string message)
    {
        message = HttpUtility.HtmlEncode(message);
        
        var connectId = UserHandler.ConnectedIds.Where(p => p.Value == userName).FirstOrDefault().Key;
        Clients.Clients(connectId).alertMessage(message); //alertMessage 為client端 function名稱
        
    }


    //發送訊息至群組
    public void sendGroupMessage(string groupId, string message)
    {
        message = HttpUtility.HtmlEncode(message);
        Clients.Group(groupId).alertMessage(message); //alertMessage 為client端 function名稱
    }

    //當使用者斷線時呼叫
    public override Task OnDisconnected(bool stopCalled)
    {
        //當使用者離開時,移除在清單內的 ConnectionId
        Clients.All.removeList(Context.ConnectionId);
        UserHandler.ConnectedIds.Remove(Context.ConnectionId);
        return base.OnDisconnected(stopCalled);
    }

}

 

 
我是統一放在App_code資料夾底下,方便修改
 
因新版本使用Owin所以在web.config要設定內容為

 <appSettings>
<!--signalR-->
    <add key="owin:AutomaticAppStartup" value="true"/>
    <add key="owin:AppStartup" value="SignalRChat.Startup" /><!--value同Startup.cs的namespace.class-->
  </appSettings>

server端的設定

接下來在前端網頁


<script src="Scripts/jquery-1.11.0.min.js"  type="text/javascript"></script>
<script src="Scripts/jquery.signalR-2.2.0.js" type="text/javascript"></script>
<%--很重要的參考,一定要加這一行,這之前一定要先參考jQuery.js與signalR.js--%>
<script src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>

<script type="text/javascript">
   
    signalRChat = $.connection.signalRChat; 

    if (signalRChat) { //判斷是否有建立成功
        registerClientMethods(signalRChat);
        
        $.connection.hub.start().done(function () {
            signalRChat.server.userConnected(groupId, userId);  //向後端註冊 Clinet端(前端)使用者的 function
        });

    }
    //*** 向後端的Hub,註冊 Clinet端(前端)的 function
    function registerClientMethods(chatHub) {
        // 定義client端的javascript function,供server端hub,透過dynamic的方式,呼叫所有Clients的javascript function
        chatHub.client.alertMessage = function (message) {
            //當server端調用addMessage時,將server push的message資料,alert出來
            alert(message);
        };
    }
    //**********************************************(end)

    $.connection.hub.disconnected(function () {
            setTimeout(function () {
                $.connection.hub.start().done(function () {
                    signalRChat.server.userConnected(groupId, userId);  //斷線後重新註冊
                 });
            }, 5 * 1000); // Restart connection after 5 seconds.

        });

</script>

 


<script src='<%= ResolveClientUrl("~/signalr/hubs") %>'></script>

上面這段也可改用以下方式


var signalRUrl = window.location.origin;
    if (!signalRUrl) { //部分ie 抓不到 window.location.origin
        signalRUrl = window.location.protocol + "//" + window.location.hostname + (window.location.port ? ':' + window.location.port : '');
    }

    $.ajax({
        url: signalRUrl + "/signalr/hubs",
        dataType: "script",
        async: false
    });

 

到這邊總算是建立完成,接下來就可以開始使用了
 
當想要對其他人發送訊息時,直接再js呼叫sever端function就可以了
例如跟全部的人說 你上線了

signalRChat.server.sendAllMessage('hello, ' + userId + ' 上線了');
使用時有幾點要特別注意
signalR 太久沒用還是會斷線的。
所以發送訊息前 可以先用以下語法檢查目前的連線狀態
 

signalRChat.connection.state   //Object {connecting: 0, connected: 1, reconnecting: 2, disconnected: 4}

 

或者是紀錄一下錯誤訊息

signalRChat.server.sendAllMessage('hello, ' + userId + ' 上線了').fail(function (error) {
        console.log('Send signalR failed. Error: ' + error);
 });
 
總結:
基本範例就照上面的作就OK了
因為.NET把signalR這功能封裝的很好,很簡單就可以學會並應用。