【SignalR學習系列】6. SignalR Hubs Api 詳解(C# Server 端)

来源:http://www.cnblogs.com/Soulless/archive/2017/07/26/7235524.html
-Advertisement-
Play Games

如何註冊 SignalR 中間件 為了讓客戶端能夠連接到 Hub ,當程式啟動的時候你需要調用 MapSignalR 方法。 下麵代碼顯示瞭如何在 OWIN startup 類裡面定義 SignalR Hubs 路由。 The /signalr URL 預設情況下,客戶端都是通過 "/signalr ...


如何註冊 SignalR 中間件

為了讓客戶端能夠連接到 Hub ,當程式啟動的時候你需要調用 MapSignalR 方法。

下麵代碼顯示瞭如何在 OWIN startup 類裡面定義 SignalR Hubs 路由。

using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(MyApplication.Startup))]
namespace MyApplication
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Any connection or hub wire up and configuration should go here
            app.MapSignalR();
        }

    }
}

 

The /signalr URL

預設情況下,客戶端都是通過 "/signalr" 路由地址來連接到你的 Hub,你也可以修改不使用預設的 "/signalr"。

服務端代碼指定Url

app.MapSignalR("/signalrTest", new HubConfiguration());

JavaScript 客戶端代碼指定Url

<script src="signalrTest/hubs"></script>

var chat = $.connection.chatHub;
chat.url = "/signalrTest";

 .NET 客戶端代碼指定Url

var Connection = new HubConnection("http://localhost:8080/signalrTest");

 

如何創建並使用 Hub 類

為了創建 Hub 類,你需要創建一個繼承於 Microsoft.Aspnet.Signalr.Hub 的類

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

 

JavaScript 端的 Hub 名字

Server (駝峰命名法,第一個字母可以為大寫也可以為小寫)

public class ContosoChatHub : Hub

JavaScript (不管 Server 端第一個字母為大小還是小寫,JavaScript 客戶端必須是小寫,不然就找不到對應的 Hub)

var contosoChatHubProxy = $.connection.contosoChatHub;

 當使用 特性 HubName命名 Hub 的名字,那麼 Server 端和 Client 端的 Hub 名大小寫必須保持一致。

 Server (如果 HubName 指定的第一個字母為大寫,那麼 JavaScript 端也必須為大寫。如果是小寫,那麼 JavaScript 端也必須為小寫)

[HubName("PascalCaseContosoChatHub")]
public class ContosoChatHub : Hub

JavaScript 

var contosoChatHubProxy = $.connection.PascalCaseContosoChatHub;

 

強類型的 Hub

 定義一個介面 T,讓你的 Hub 類繼承於 Hub<T>,這樣就可以指定你的客戶端可以調用的方法,也可以在你的 Hub 方法里開啟代碼提示。

public class StrongHub : Hub<IClient>
{
    public void Send(string message)
    {
        Clients.All.NewMessage(message);
    }
}

public interface IClient
{
    void NewMessage(string message);
}

 

如何在 Hub 類里定義客戶端可以調用的方法

如果需要暴露一個可以在客戶端調用的方法,那麼需要在 Hub 里定義一個 public 的方法,如下所示。

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}
public class StockTickerHub : Hub
{
    public IEnumerable<Stock> GetAllStocks()
    {
        return _stockTicker.GetAllStocks();
    }
}

你可以指定方法的參數和返回類型,包含複雜類型和數組。這些數據在客戶端和服務端會使用 Json 來進行傳輸,SignalR 會自動綁定複雜類型對象和數組對象。

 

Hub 里的方法名(非客戶端方法名)

Server (駝峰命名法,第一個字母可以為大寫也可以為小寫)

public void NewContosoChatMessage(string userName, string message)

JavaScript (不管 Server 端第一個字母為大小還是小寫,JavaScript 客戶端必須是小寫,不然就找不到對應的 方法)

contosoChatHubProxy.server.newContosoChatMessage(userName, message);

使用特性 HubMethodName 指定方法名,那麼 Server 端和 Client 端的 方法名大小寫必須保持一致。

Server 端

[HubMethodName("PascalCaseNewContosoChatMessage")]
public void NewContosoChatMessage(string userName, string message)

JavaScript 端

contosoChatHubProxy.server.PascalCaseNewContosoChatMessage(userName, message);

 

如何在 Hub 類中調用客戶端的方法

你可以在 Hub 類方法中使用 Clients 屬性來調用客戶端的方法

Server 端

public class ContosoChatHub : Hub
{
    public void NewContosoChatMessage(string name, string message)
    {
        Clients.All.addNewMessageToPage(name, message);
    }
}

JavaScript 端

contosoChatHubProxy.client.addNewMessageToPage = function (name, message) {
    // Add the message to the page. 
    $('#discussion').append('<li><strong>' + htmlEncode(name)
        + '</strong>: ' + htmlEncode(message) + '<li>');
};

你不能從客戶端得到一個返回值,比如 int x = Clients.All.add(1,1) 是沒有用的。

你可以給參數指定複雜類型和數組類型,如下所示。

Sever 端

public void SendMessage(string name, string message)
{
    Clients.All.addContosoChatMessageToPage(new ContosoChatMessage() { UserName = name, Message = message });
}

public class ContosoChatMessage
{
    public string UserName { get; set; }
    public string Message { get; set; }
}

JavaScript 代碼

var contosoChatHubProxy = $.connection.contosoChatHub;
contosoChatHubProxy.client.addMessageToPage = function (message) {
    console.log(message.UserName + ' ' + message.Message);
});

 

指定哪些客戶端接收信息

所有連接的客戶端

Clients.All.addContosoChatMessageToPage(name, message);

只是 Calling 的客戶端

Clients.Caller.addContosoChatMessageToPage(name, message);

所有連接的客戶端除了 Calling 的客戶端

Clients.Others.addContosoChatMessageToPage(name, message);

通過 connection ID 指定特定的客戶端

Clients.Client(Context.ConnectionId).addContosoChatMessageToPage(name, message);

通過 connection ID 排除特定的客戶端

Clients.AllExcept(connectionId1, connectionId2).addContosoChatMessageToPage(name, message);

指定一個特殊組

Clients.Group(groupName).addContosoChatMessageToPage(name, message);

指定一個特殊組,並且排除特定 connection ID 的客戶端

Clients.Group(groupName, connectionId1, connectionId2).addContosoChatMessageToPage(name, message);

指定一個特殊組,但是排除 calling

Clients.OthersInGroup(groupName).addContosoChatMessageToPage(name, message);

通過 userId 指定一個特殊的用戶,一般情況下是 IPrincipal.Identity.Name

Clients.User(userid).addContosoChatMessageToPage(name, message);

在一個 connection IDs 列表裡的所有客戶端和組

Clients.Clients(ConnectionIds).broadcastMessage(name, message);

指定多個組

Clients.Groups(GroupIds).broadcastMessage(name, message);

通過用戶名

Clients.Client(username).broadcastMessage(name, message);

一組用戶名

Clients.Users(new string[] { "myUser", "myUser2" }).broadcastMessage(name, message);

 

大小寫不敏感

客戶端方法名的調用是大小寫不敏感的,比如 Clients.All.addContosoChatMessageToPage 會調用客戶端的 AddContosoChatMessageToPage, addcontosochatmessagetopage, or addContosoChatMessageToPage 這些方法。

 

非同步執行

public async Task NewContosoChatMessage(string name, string message)
{
    await Clients.Others.addContosoChatMessageToPage(data);
    Clients.Caller.notifyMessageSent();
}

 

如何使用一個 string 變數作為方法名

如果你需要使用一個 string 作為方法名,那麼你需要把 Clients.All (or Clients.Others, Clients.Caller, etc.)  對象賦值給 IClientProxy,然後調用它的 Invoke(methodName, args...) 方法。

public void NewContosoChatMessage(string name, string message)
{
    string methodToCall = "addContosoChatMessageToPage";
    IClientProxy proxy = Clients.All;
    proxy.Invoke(methodToCall, name, message);
}

 

管理組成員關係

Server 端

public class ContosoChatHub : Hub
{
    public Task JoinGroup(string groupName)
    {
        return Groups.Add(Context.ConnectionId, groupName);
    }

    public Task LeaveGroup(string groupName)
    {
        return Groups.Remove(Context.ConnectionId, groupName);
    }
}

客戶端

contosoChatHubProxy.server.joinGroup(groupName);
contosoChatHubProxy.server.leaveGroup(groupName);

非同步執行

public async Task JoinGroup(string groupName)
{
    await Groups.Add(Context.ConnectionId, groupName);
    Clients.Group(groupname).addContosoChatMessageToPage(Context.ConnectionId + " added to group");
}

 

如何在 Hub 類裡面捕獲和處理連接生命周期事件

public class ContosoChatHub : Hub
{
    public override Task OnConnected()
    {
        // Add your own code here.
        // For example: in a chat application, record the association between
        // the current connection ID and user name, and mark the user as online.
        // After the code in this method completes, the client is informed that
        // the connection is established; for example, in a JavaScript client,
        // the start().done callback is executed.
        return base.OnConnected();
    }

    public override Task OnDisconnected()
    {
        // Add your own code here.
        // For example: in a chat application, mark the user as offline, 
        // delete the association between the current connection id and user name.
        return base.OnDisconnected();
    }

    public override Task OnReconnected()
    {
        // Add your own code here.
        // For example: in a chat application, you might have marked the
        // user as offline after a period of inactivity; in that case 
        // mark the user as online again.
        return base.OnReconnected();
    }
}

 

如何從 Content 裡面獲取 Clients 信息

Calling 客戶端的 connection ID

string connectionID = Context.ConnectionId;

connection ID 是一個由SignalR分配的 GUID  ( 你不能用自己的代碼指定 ). 每個連接都有一個 connection ID , 如果你的應用里包含多個 Hubs,那麼多個 Hubs也會共用同一個 connection ID .

Http Header 數據

System.Collections.Specialized.NameValueCollection headers = Context.Request.Headers;
System.Collections.Specialized.NameValueCollection headers = Context.Headers;

Query string 數據

System.Collections.Specialized.NameValueCollection queryString = Context.Request.QueryString;
System.Collections.Specialized.NameValueCollection queryString = Context.QueryString;
string parameterValue = queryString["parametername"];

JavaScript 客戶端 QueryString

$.connection.hub.qs = { "version" : "1.0" };

這邊的 $.connection.hub.qs 不是指你當前的 Hub ($.connection.chatHub.qs)。

Cookies

System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.Request.Cookies;
System.Collections.Generic.IDictionary<string, Cookie> cookies = Context.RequestCookies;

用戶信息

System.Security.Principal.IPrincipal user = Context.User;

Request 的 HttpContext 對象

System.Web.HttpContextBase httpContext = Context.Request.GetHttpContext();

 

如何在客戶端和服務端傳遞 State

JavaScript 客戶端

contosoChatHubProxy.state.userName = "Fadi Fakhouri";
contosoChatHubProxy.state.computerName = "fadivm1";

Server 端獲取,可以使用 Caller 或者 CallerState 兩種方式

string userName = Clients.Caller.userName;
string computerName = Clients.Caller.computerName;
string userName = Clients.CallerState.userName;
string computerName = Clients.CallerState.computerName;

 

如何在 Hub 類中捕獲異常

  • 使用 try - catch 並記錄日誌
  • 創建一個能處理OnIncomingError的 Hubs 管道模型
    • public class ErrorHandlingPipelineModule : HubPipelineModule
      {
          protected override void OnIncomingError(ExceptionContext exceptionContext, IHubIncomingInvokerContext invokerContext)
          {
              Debug.WriteLine("=> Exception " + exceptionContext.Error.Message);
              if (exceptionContext.Error.InnerException != null)
              {
                  Debug.WriteLine("=> Inner Exception " + exceptionContext.Error.InnerException.Message);
              }
              base.OnIncomingError(exceptionContext, invokerContext);
      
          }
      }
      
      public void Configuration(IAppBuilder app)
      {
          // Any connection or hub wire up and configuration should go here
          GlobalHost.HubPipeline.AddModule(new ErrorHandlingPipelineModule()); 
          app.MapSignalR();
      }
  • 使用 HubException 類,拋出異常
    • public class MyHub : Hub
      {
          public void Send(string message)
          {
              if(message.Contains("<script>"))
              {
                  throw new HubException("This message will flow to the client", new { user = Context.User.Identity.Name, message = message });
              }
      
              Clients.All.send(message);
          }
      }

 

啟用日誌診斷

如果需要啟動 Server 端日誌,那麼需要在 web.config 裡面添加一個 system.diagnostics 節點

<system.diagnostics>
    <sources>
      <source name="SignalR.SqlMessageBus">
        <listeners>
          <add name="SignalR-Bus" />
        </listeners>
      </source>
     <source name="SignalR.ServiceBusMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
     </source>
     <source name="SignalR.ScaleoutMessageBus">
        <listeners>
            <add name="SignalR-Bus" />
        </listeners>
      </source>
      <source name="SignalR.Transports.WebSocketTransport">
        <listeners>
          <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.ServerSentEventsTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.ForeverFrameTransport">
          <listeners>
              <add name="SignalR-Transports" />
          </listeners>
      </source>
      <source name="SignalR.Transports.LongPollingTransport">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
      <source name="SignalR.Transports.TransportHeartBeat">
        <listeners>
            <add name="SignalR-Transports" />
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="SignalRSwitch" value="Verbose" />
    </switches>
    <sharedListeners>
      <add name="SignalR-Transports" 
           type="System.Diagnostics.TextWriterTraceListener" 
           initializeData="transports.log.txt" />
        <add name="SignalR-Bus"
           type="System.Diagnostics.TextWriterTraceListener"
           initializeData="bus.log.txt" />
    </sharedListeners>
    <trace autoflush="true" />
  </system.diagnostics>

 

如何在 Hub 類外面調用客戶端方法和管理組

如果需要在 Hub 類外面調用客戶端方法和管理組,那麼需要獲取一個 SignalR context 對象,然後可以通過 context.Clients 對象來操作客戶端方法和組。

IHubContext _context = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
_context.Clients.All.updateStockPrice(stock);

 

如何自定義管道模型

SignalR 允許你把自己的代碼註入 Hub 管道模型,你可以通過繼承 HubPipelineModule 來實現

public class LoggingPipelineModule : HubPipelineModule 
{ 
    protected override bool OnBeforeIncoming(IHubIncomingInvokerContext context) 
    { 
        Debug.WriteLine("=> Invoking " + context.MethodDescriptor.Name + " on hub " + context.MethodDescriptor.Hub.Name); 
        return base.OnBeforeIncoming(context); 
    }   
    protected override bool OnBeforeOutgoing(IHubOutgoingInvokerContext context) 
    { 
        Debug.WriteLine("<= Invoking " + context.Invocation.Method + " on client hub " + context.Invocation.Hub); 
        return base.OnBeforeOutgoing(context); 
    } 
}

然後在 Startup.cs 里註入自定義的 Hub 管道模型

public void Configuration(IAppBuilder app) 
{ 
    GlobalHost.HubPipeline.AddModule(new LoggingPipelineModule()); 
    app.MapSignalR();
}

 

參考鏈接:

https://docs.microsoft.com/zh-cn/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 對於使用集群,負載均衡組件是必不可少的。包括在應用層和服務層都可以使用負載均衡。 負載均衡 我瞭解的負載均衡分兩種,使用硬體:F5和A10,和使用開源軟體:Nginx和LVS。 這裡先做個記錄,後面研究了之後再來補充。 ...
  • 搜索引擎在大型網站中也非常重要,尤其是對於大數據的處理方面,比如日誌的實時收集和分析。 搜索引擎 我研究的是ElasticSearch,最好要研究整個ELK解決方案。 先記錄在這裡,等研究完了再來完善!!! ...
  • 引言 因為工作內容的原因需要相容 XP,而 XP 最多支持到.Net Framework 4.0。因此無法享受到 .Net Core 帶來的一堆很好用的庫,好在無論 corefx 還是 Asp.Net Core 都是開源的,我們可以自行修改編譯出支持 .Net 4.0 的版本。 技術可行性 Net ...
  • NoSQL最常見的解釋是non-relational,或者not only SQL,從欄位意思上就可以看出,它是指非關係型資料庫的統稱。 NoSQL誕生的背景 隨著大型網站分散式架構的發展,使用傳統關係型資料庫在以下幾個方面顯得力不從心: 1,對資料庫高併發讀寫 隨著分散式架構和微服務的流行,資料庫 ...
  • 13.1 類和介面繼承 13.2 定義介面 C 用 interface 關鍵字定義介面.介面中可定義方法,事件,無參屬性和有參屬性(C 的索引器),但不能定義任何構造器方法,也不能定義任何實例欄位. 13.3 繼承介面 C 編譯器要求將實現介面的方法(簡稱為"介面方法")標記為public. CLR ...
  • https://netftp.codeplex.com/SourceControl/latest http://ftplib.codeplex.com/ https://www.codeproject.com/articles/18537/c-ftp-client-library https://w ...
  • 程式不複雜,直接上代碼了: 運行效果如下: ...
  • 有個項目需要調用第三方SDK,而SDK獲取伺服器的已安裝的特殊印表機列表返回給調用方。 但我不想依賴這個SDK,因為這個SDK是使用.NET Framework編寫的,而我的項目是使用.NET Core編寫的,並且想要部署在Docker容器內運行。 於是反編譯了SDK,查看源代碼,看到該SDK調用了 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...