2018 年 5 月

第 33 卷,第 5 期

本文章是由機器翻譯。

技術最前線 - 一窺 ASP.NET Core SignalR 究竟

Dino Esposito

Dino EspositoSignalR 是最新加入的 ASP.NET 核心平台,以及的長時間等候一看它。我敢說,只是現在使用 SignalR 上可以我們真正開始了解 ASP.NET Core 為準備的所有類型的應用程式的質數時間。任何嚴重 Web 應用程式目前也不不某種形式的非同步通知和即時的功能。

您知道從 ASP.NET SignalR 完全重寫,而現在會位於 ASP.NET Core 程式庫的系列。我的資料行上個月中 (msdn.com/magazine/mt846469),我會提供功能的快速教學和新的 ASP.NET Core SignalR 的功能。在此資料行,我將探討內部的機制。

SignalR 是雙向,雙向遠端程序呼叫 (RPC) 的抽象層,而且會透過各種不同的傳輸通訊協定運作。它是主機無從驗證並不限於 HTTP。在最新版本中,它可以傳輸二進位資料並不只 JSON 訊息。在設定 SignalR ASP.NET 或 ASP.NET Core 時,您可以選取的傳輸通訊協定和訊息通訊協定。如果您沒有進行明確的選擇,會自動選擇的傳輸通訊協定,大部分的情況下它其實 WebSockets。訊息通訊協定,相反地,會根據 JSON。讓我們看看用戶端時,會發生什麼事 — 例如,Web 用戶端,設定與伺服器端點的連線。下列程式碼會啟動連線。

var progressConnection = new signalR.HubConnection("/progressDemo");
progressConnection.start();

如果您要監視此程式碼使用 Fiddler 等工具所產生的 Web 流量,您會看到兩個 HTTP 要求會傳送。圖 1顯示 bootstraps 交談的第一個要求的詳細資料。

要求中初始啟動程式的詳細資訊
圖 1 詳細資料,初始啟動程式的要求

初始要求的目標中啟動類別後面加上指定的 SignalR 路由的 HTTP POST / 交涉區段。下列程式碼會示範如何定義 SignalR 路由:

app.UseSignalR(routes =>
{
  routes.MapHub<ProgressHub>("/progressDemo");
});

根據路由,第一次呼叫會以目標 URL: / progressdemo/交涉。

請注意在預覽版本中的 SignalR 選項動詞命令會使用針對相同目的。SignalR 的伺服器端點會傳回 JSON 物件,如下所示設定:

{
  "connectionId" : "b6668ac0-1083-499f-870a-2a5376bf5047",
  "availableTransports" : [
    "WebSockets", "ServerSentEvents", "LongPolling"
  ]
}

如您所見,JSON 回應包含兩件事: 剛建立的連線,以及一份可供使用的傳輸通訊協定的唯一識別碼。範例程式碼表示 WebSockets、 ServerSentEvents 和 LongPolling 可以使用用戶端和伺服器組態。接下來的情況,取決於實際選擇的用戶端傳輸。

SignalR 往往會儘可能使用 WebSockets。如果沒有,它會檢查 ServerSentEvents 和之後,就會切換回 LongPolling。如果可以使用 WebSockets,HTTP GET 要求會以與之前相同的 URL。目前要求也會將連接識別碼做為查詢字串參數。GET 要求是實際的通訊協定升級要求。更明確地說,通訊協定升級為標準 HTTP 要求 (GET,但它也可以是 POST) 具有兩個特定的標頭。其中一個是連接標頭,必須設定為升級。另一個方法是升級,必須設定為所需的通訊協定的名稱 — 在此情況下,WebSockets。如果接受的通訊協定升級,則伺服器會傳回 HTTP 101 切換通訊協定回應 (如中所示圖 1)。

傳輸通訊協定

SignalR 可採用各種不同的傳輸通訊協定。用戶端可以強制執行特定的通訊協定,透過連線,但依預設自動判斷的通訊協定。若要要求特定的傳輸,只要加入至 JavaScript 程式碼 (或如果使用非 Web 用戶端的用戶端程式碼) 的額外參數。以下為範例:

var progressConnection = new signalR.HubConnection(
  "/progressDemo",
  {transport : signalR.TransportType.WebSocket});

請注意,藉由傳遞陣列而不是直接的名稱,您可以選擇限制其中幾個指定的通訊協定。當然,在幾個層面與不同的傳輸通訊協定。圖 2列出每個通訊協定的主要特性。

圖 2 支援傳輸通訊協定

通訊協定 描述
WebSockets 根據用戶端和伺服器之間的一對一連線。用戶端和伺服器上撰寫共用管道,讓資料流量的雙向。通訊協定不能用在所有位置,並會受到在瀏覽器和伺服器連線使用。在用戶端,新式瀏覽器需要。所有一般的瀏覽器的最新版本通常運作,除了在第 10 版之前的 Internet Explorer。在伺服器端,使用 IIS 或 HttpSysServer 時,用戶端上需要 Windows 8 或更新版本。
ServerSentEvents 在伺服器上作用的 EventSource 物件為基礎。物件代表伺服器連接至用戶端的管道。一旦建立連接之後,伺服器可以持續傳送事件時的用戶端進行通訊只能透過一般的 AJAX 呼叫。通訊協定回 Netscape 早期的日期,並不支援 Internet Explorer 或 Edge 瀏覽器。
LongPolling 通訊協定的運作方式是開啟連接的伺服器做為未來的回應。連線處於擱置狀態之前傳送回應,或要求逾時。在任何情況下,一旦關閉連接,用戶端立即重新建立它,讓輪詢是連續的但僅限於什麼是絕對必要的流量。此通訊協定適用於所有版本的所有瀏覽器,並會被視為後援方案。

如果 Web 用戶端與伺服器之間的通訊是跨網域,則伺服器必須啟用 CORS。在此情況下,您可以使用任何可用的通訊協定 (請注意 JSONP 不支援的 ASP.NET Core SignalR):

public void ConfigureServices(IServiceCollection services)
{
  services.AddCors();
}

瀏覽器會將 AJAX 和 WebSockets 呼叫 ASP.NET Core SignalR 的 Origin 標頭。此外,您需要有在您的 SignalR 應用程式,以確保瀏覽器可讓要求中設定的 CORS 中介軟體。

訊息通訊協定

在 ASP.NET 中永遠會序列化 SignalR 交換使用以文字為基礎的 JSON 格式的訊息。最新的 ASP.NET Core SignalR 加入根據 MessagePack 格式的二進位訊息通訊協定。此二進位序列化格式可讓您以語言無關的方式,JSON 並未的方式交換資料。

MessagePack 不更 compact,要傳送 JSON,比更快和更多的功能封包大小壓縮比二進位 JSON (BSON) — MongoDB 最佳化類別的 JSON。在 MessagePack,小整數與 NULL 值會耗用只有一個位元組的資料。相較之下,JSON Null 會耗用 4 個位元組。如需有關通訊協定的詳細資訊,請參閱msgpack.org

若要從 ASP.NET Core SignalR Web 用戶端使用 MessagePack,您加入一個詳細的參數設定的連線,JavaScript 程式碼如下所示:

var protocol = new signalR.protocols.msgpack.MessagePackHubProtocol();
var progressConnection = new signalR.HubConnection(
  "/progressDemo",
  {
    transport : signalR.TransportType.WebSocket,
    protocol : protocol
  }
);

請注意,在 Web 用戶端願意使用 MessagePack,您也必須包含的個別 JavaScript 檔案 (signalr msgpackprotocol.min.js) 您 @aspnet/signalr NPM 套件中找到。MessagePack 也需要 ASP.NET Core 伺服器設定,就像這樣:

public void ConfigureServices(IServiceCollection services)
{
  // SignalR already configured. Just add this:
  services.AddMessagePackProtocol();
}

如果您使用了非 Web 用戶端,您只需要啟用 MessagePack 執行會是一個額外的呼叫中用戶端應用程式。具體來說,您會放置 WithMessagePackProtocol 類別 HubConnectionBuilder 上定義擴充方法的呼叫。

非 Web 用戶端

.NET 標準規格最有趣的層面是,只要應用程式開發介面相容性存在,您可以使用相同的媒體櫃從各種不同的用戶端應用程式內。ASP.NET Core SignalR 用戶端程式庫,根據.NET Standard 2.0 中,可用於從編譯相容的平台,包括 Microsoft.NET Framework 4.6.1 的各種不同的任何用戶端應用程式及更新版本。這表示,例如,編譯的較新的.NET framework 版本比 4.6 愉快地保存他們可以取用的服務的 ASP.NET Core SignalR 中樞的 Windows Form 應用程式。

這一點之後,讓我們了解如何啟動長時間上個月的資料行中所討論的工作,並監視從新的 Windows Form 應用程式中。若要開始建立應用程式的基本架構中之後, 參考名為 Microsoft.AspNetCore.SignalR.Client 和所有其相依性的 NuGet 套件,就像這樣:

private static HubConnection _connection;
private async void Form1_Load(object sender, EventArgs e)
{
  _connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:60000/progressdemo")
    .Build();
  await _connection.StartAsync();
}

載入的表單,並建立與指定的 SignalR 端點連線時,就會執行程式碼。如果您想要使用 MessagePack 通訊協定序列化交換資料,加入另一行連接產生器物件,組態程式碼,如下所示:

private async void Form1_Load(object sender, EventArgs e)
{
  _connection = new HubConnectionBuilder()
    .WithUrl("http://localhost:60000/progressdemo")
    .WithMessagePackProtocol()
    .Build();
  await _connection.StartAsync();
}

您會收到連接物件必須使用用戶端處理常式負責重新整理 UI,一旦從 ASP.NET Core SignalR 端點通知回來進一步設定。該程式碼如下:

_connection.On<int>("updateProgressBar", (perc) =>
{
  this.Invoke(
    (Action) (() => label1.Text = String.Format("{0}%", perc))
});

範例程式碼中收到 updateProgressBar 通知時,文字標籤更新接收到的值表示目前完成工作的百分比。您可以為您最多的處理常式需要和伺服器端 SignalR 對應項目會公開。圖 3會顯示最後一個月資料行 SignalR 後端,完全重寫如下,Windows Form 用戶端會使用它。

圖 3 重寫的 SignalR 後端

_connection.On("initProgressBar", () =>
{
  // Run on the UI thread
  Invoke((Action)(() => label1.Text = "0%"));
});
_connection.On<int>("updateProgressBar", (perc) =>
{
  // Run on the UI thread
  Invoke((Action) (() => label1.Text = String.Format("{0}%", perc)));
});
_connection.On("clearProgressBar", () =>
{
  // Run on the UI thread
  Invoke((Action)(() =>
  {
    label1.Text = "100%";
    button1.Enabled = true;
  }));
});
await _connection.StartAsync();

有幾個您應該注意的撰寫非 Web SignalR 用戶端時的問題。首先,用戶端處理常式時,必須完全設定啟動連線。具體而言,這表示 StartAsync 呼叫應該在所有到期 < T > 上之方法呼叫。

第二,請記住,您不想要執行伺服器端,並可能發生冗長 — SignalR 會通知在相同的 Windows UI 執行緒的作業。這會讓用戶端應用程式沒有回應。若要暫時解決此問題,您必須啟動另一個執行緒,如下所示的伺服器端作業:

private void button1_Click(object sender, EventArgs e)
{
  Task.Run(() =>
  {
    var client = new WebClient();
    client.UploadString("http://localhost:60000/task/lengthy);
  });
}

接著,當伺服器端 SignalR 中樞傳回通知,到期的任何變更必須傳達給主要 UI 執行緒之後才能進行。用戶端處理常式中使用叫用方法達到這個目的。叫用方法取得的委派,並執行它,在 UI 執行緒上,如下所示:

Invoke((Action)((perc) =>
  {
    label1.Text = String.Format("{0}%", perc);
  }));

圖 4範例 Windows Form 應用程式顯示作用中,伺服器端作業取得任何進展,逐漸時更新的標籤。

更新 ASP.NET Core SignalR 中樞的 Windows Form 用戶端應用程式
圖 4 的 Windows Form 更新 ASP.NET Core SignalR 中樞的用戶端應用程式

在 Windows Forms 應用程式可以接收通知,透過任何支援方法: 廣播、 直接連接、 群組、 單一使用者及資料流。我將有更多的看法這在未來的資料行中。

總結

ASP.NET Core SignalR 支援與先前的 ASP.NET 版本,包括 WebSockets、 ServerSentEvents 和 LongPolling 相同的傳輸通訊協定。此外,它支援除了標準 JSON 格式的二進位訊息通訊協定。

可以從各種不同的用戶端呼叫如同其前身,ASP.NET Core SignalR — 包括傳統的 Windows Form 應用程式。要達到廣泛的相容性的索引鍵是支援的.NET 標準 2.0 規格。如同我們在文章中,如果用戶端不是.NET Core 應用程式,必須將其編譯至最新的標準與相容的.NET framework 版本。所需的最低版本是.NET Framework 4.6.1。

請務必簽出,請參閱這篇文章的原始程式碼bit.ly/2FxCKTs


Dino Esposito20 個以上的書籍和 1000 生涯 25 年中的文章中有撰寫。作者"Sabbatical 中斷,"theatrical 樣式節目,Esposito 忙將甚至全世界的軟體撰寫成在 BaxEnergy 數位策劃師。在 Twitter 上關注他: @despos

非常感謝下列 Microsoft 專家檢閱這篇文章:Andrew Stanton 護士


MSDN Magazine 論壇中的這篇文章的討論