史上最全面的SignalR系列教程-3、SignalR 實現推送功能-集線器類實現方式

  • 2019 年 10 月 3 日
  • 筆記

1、概述

通過前兩篇

史上最全面的SignalR系列教程-1、認識SignalR

史上最全面的SignalR系列教程-2、SignalR 實現推送功能-永久連接類實現方式

RDIFramework.NET敏捷開發框架通過SignalR技術整合即時通訊(IM)

文章對SignalR的介紹,我們對SignalR已經有了全面的認識。SignalR API 客戶端和伺服器端持久連接的通訊方式,一次連接代表一個發送單個、分組或者廣播消息的簡單終端。持久連接的API(表現在.NET的PersistentConnection 類上)給了開發人員低價訪問SignalR所暴露的通訊協議的條件。使用這種連接方式,就像開發人員使用WCF一樣。

本篇將繼續在上一篇的基礎上,講解SignalR通過最常用的集線器方式實現消息推送與發送。
我們知道SignalR的通訊模型主要是兩類Persistent Connections與Hubs。Hub是一種更高級的管道,它在連接協議上允許客戶端和伺服器端能夠直接調用彼此的方法。SignalR的這種自動分發跨機器邊界調度的方法就像施了魔法一樣,讓客戶端調用服務端的方法像調用本地一樣簡單,反之亦然。使用Hub的模式就像開發人員使用遠程API一樣,比如 .NET Remoting。使用Hub同樣能夠讓你傳遞類型化的參數到方法上進行模型綁定。通過Hubs實現服務端消息推送到客戶端,抽象結構圖如下。

SignalR Hub 結構流程圖

2、SignalR Hub 原理分析

SignalR具體是如何到達實行性的呢?SignalR 的實現機制與 .NET WCF 或 Remoting 是相似的,都是使用遠程代理來實現。SignalR 將整個連接,資訊交換過程封裝得非常漂亮,客戶端與伺服器端全部使用 JSON 來交換數據。

當服務端的程式碼訪問一個客戶端的方法時,一個數據包被自動傳輸,數據包中包含了函數方法參數的名稱(如果是一個對象,那麼這個對象會被序列化成JSON)。客戶端然後根據客戶端的程式碼匹配方法的名稱。如果找到相應的匹配方法,那麼久調用相應的函數執行反序列化的參數。

3、Hubs實現實時消息流程

  • 在伺服器端定義對應的hub class;

  • 在客戶端定義hub class 所對應的 proxy 類;

  • 在客戶端與伺服器端建立連接(connection);

  • 然後客戶端就可以調用 proxy 對象的方法來調用伺服器端的方法,也就是發送 request 給伺服器端;

  • 伺服器端接收到 request 之後,可以針對某個/組客戶端或所有客戶端(廣播)發送消息。

SignalR Hubs通訊模型流程

4、SignalR的Hub連接類Mvc實現

我們繼續在上一篇項目基礎上擴展hubs的方式的使用。具體新增項目、添加signalr引用等可以參考上一篇。

4.1、向工程中添加HubConnections目錄,在其中添加ChatHub.cs文件,如下圖所示:

新建charthub文件

程式碼內容如下:

using Microsoft.AspNet.SignalR;  using Microsoft.AspNet.SignalR.Hubs;    namespace SignalRTestProj.HubConnections  {      //HubName 這個特性是為了讓客戶端知道如何建立與伺服器端對應服務的代理對象,      //如果沒有設定該屬性,則以伺服器端的服務類名字作為 HubName 的預設值      [HubName("chat")]      public class ChatHub : Hub      {          public void Send(string clientName, string message)          {              // Call the addSomeMessage method to update clients.              Clients.All.addSomeMessage(clientName, message);          }      }  }

在上面的程式碼中,實現的服務很簡單,就是當一個客戶端調用Send方法向伺服器發送message後,伺服器端負責將該 message廣播給所有的客戶端(也可以給特定組或特定客戶端),以實現聊天室的功能。

除了服務端可以向所有客戶端通知調用客戶端方法之外,還可以對其中想要發送的客戶端進行限制。同時Clients這個屬性有很多動態成員供我們使用:

Clients.All:允許「調用」連接到此Hub上的所有客戶端的一個方法    Clients.AllExcept:表示該調用必須發送給所有客戶端,但是除了那些作為參數的connectionId以外。這裡的參數可以是connectionId字元串、數組等    Clients.Caller 確定調用者的接收者是目前調用正在執行Hub方法的客戶端    Clients.Client:將對方法的調用發送給指定connectionId的客戶端,參數可以是字元串,也可以是數組    Client.Others :代表所有已連接的客戶端,但是不包括正在調用該方法的客戶端。    在方法中可以通過訪問 this.Context.ConnectionId來獲得當前掉用方法的客戶端唯一標識符

1)、HubName 這個特性是為了讓客戶端知道如何建立與伺服器端對應服務的代理對象,如果沒有設定該屬性,則以伺服器端的服務類名字作為 HubName 的預設值;

2)、ChatHub 繼承自 Hub,從下面 Hub 的介面圖可以看出:Hub 支援向發起請求者(Caller),所有客戶端(Clients),特定組(Group) 推送消息。

Hub對象定義關係圖

3)、public void Send(string clientName, string message) 這個介面是被客戶端通過代理對象調用的;

4)、Clients 是 Hub 的屬性,表示所有鏈接的客戶端頁面,它和 Caller一樣是 dynamic,因為要直接對應到 Javascript 對象;

5)、Clients.All.addSomeMessage(clientName, message): 表示伺服器端調用客戶端的 addSomeMessage 方法,這是一個 Javascript 方法,從而給客戶端推送消息。

4.2、配置啟動類

using Microsoft.Owin;  using Owin;  [assembly: OwinStartup(typeof(SignalRTestProj.App_Start.ChartStartup))]    namespace SignalRTestProj.App_Start  {      public class ChartStartup      {          public void Configuration(IAppBuilder app)          {              // 有關如何配置應用程式的詳細資訊,請訪問 https://go.microsoft.com/fwlink/?LinkID=316888              //1、 PersistentConnection 方式配置              //app.MapSignalR<ChatConnection>("/Connections/ChatConnection");                //2、hub方式配置              app.MapSignalR();          }      }  }

4.3、頁面程式碼實現

<h2>Hub Chat</h2>    <div>      <input type="hidden" id="ClientName" value="@ViewBag.ClientName"/>      <input type="text" id="msg" />      <input type="button" id="broadcast" value="廣播" />      <br />        <h3>          (<span id="MyClientName">@ViewBag.ClientName</span>):      </h3>        <ul id="messages"></ul>  </div>    @section scripts {      <script src="~/Scripts/jquery-3.3.1.min.js"></script>      <script src="~/Scripts/jquery.signalR-2.4.1.min.js"></script>      <script src="~/signalr/hubs"></script>      <script>          $(function () {              var chat = $.connection.chat;              var myClientName = $('#ClientName').val();              chat.client.addSomeMessage = function (clientName, message) {                  writeMsg('<b>' + clientName + '</b> 對大家說: ' + message, 'event-message');              };                $('#msg').focus();              // 開始連接              $.connection.hub.start().done(function () {                  $('#broadcast').click(function () {                      // 調用send方法                      chat.server.send(myClientName, $('#msg').val());                      $('#msg').val('').focus();                  });              });                //寫消息              function writeMsg(eventLog, logClass) {                  var now = new Date();                  var nowStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();                  $('#messages').prepend('<li class="' + logClass + '"><b>' + nowStr + '</b> ' + eventLog + '.</li>');              }          });      </script>  }

在上面的程式碼我們

1、首先獲取客戶端頁面的名字;

2、然後通過 $.connection.chat 建立對應伺服器端 Hub 類的代理對象 chat;

3、定義客戶端的 Javascript 方法 addSomeMessage,伺服器通過 dynamic 方式調用客戶端的該方法以實現推送功能。在這裡每當收到伺服器推送來的消息,就在客戶端頁面的 messages 列表表頭插入該消息。

4、當點擊廣播按鈕時,客戶端通過代理對象調用伺服器端的 send 方法以實現向伺服器發送消息。

5、通過 $.connection.hub.start(); 語句打開鏈接。

5、效果展示

運行效果展示

6、程式碼下載

實例源碼可以移步github下載,地址:https://github.com/yonghu86/SignalRTestProj

7、參考文章


一路走來數個年頭,感謝RDIFramework.NET框架的支援者與使用者,大家可以通過下面的地址了解詳情。

RDIFramework.NET官方網站:http://www.rdiframework.net/

RDIFramework.NET官方部落格:http://blog.rdiframework.net/

同時需要說明的,以後的所有技術文章以官方網站為準,歡迎大家收藏!

RDIFramework.NET框架由海南國思軟體科技有限公司專業團隊長期打造、一直在更新、一直在升級,請放心使用!

歡迎關注RDIFramework.net框架官方公眾微信(微訊號:guosisoft),及時了解最新動態。

掃描二維碼立即關注

微訊號:guosisoft