瞭解及處理 SignalR 1.x 中的連線存留期事件

作者 :Patrick FletcherTom Dykstra

警告

本檔不適用於最新版的 SignalR。 看看ASP.NET Core SignalR

本文提供您可以處理之 SignalR 連線、重新連線和中斷線上活動的概觀,以及您可以設定的逾時和持續設定。

本文假設您已經瞭解 SignalR 和連線存留期事件。 如需 SignalR 簡介,請參閱SignalR - 概觀 - 消費者入門。 如需連線存留期事件的清單,請參閱下列資源:

概觀

本文包含下列各節:

API 參考主題的連結是 API 的 .NET 4.5 版本。 如果您使用 .NET 4,請參閱 API 主題的 .NET 4 版本

連線存留期術語和案例

OnReconnectedSignalR Hub 中的事件處理常式可以在指定用戶端之後直接執行,但不能在 之後 OnConnectedOnDisconnected 執行。 您可以有沒有中斷連線的重新連線的原因,是有數種方式可在 SignalR 中使用「連線」一詞。

SignalR 連線、傳輸連線和實體連線

本文將區分 SignalR 連線傳輸連線實體連線

  • SignalR 連線 是指用戶端與伺服器 URL 之間的邏輯關聯性,由 SignalR API 維護,並以連線識別碼唯一識別。 此關聯性的相關資料是由 SignalR 維護,並用來建立傳輸連線。 當用戶端呼叫 Stop 方法或達到逾時限制時,當 SignalR 嘗試重新建立遺失的傳輸連線時,關聯性會結束,而 SignalR 會處置資料。
  • 傳輸連線 是指用戶端與伺服器之間的邏輯關聯性,由四個傳輸 API 之一維護:WebSockets、伺服器傳送的事件、永久框架或長時間輪詢。 SignalR 會使用傳輸 API 來建立傳輸連線,而傳輸 API 取決於實體網路連線的存在來建立傳輸連線。 當 SignalR 終止,或傳輸 API 偵測到實體連線中斷時,傳輸連線就會結束。
  • 實體連線 是指實體網路連結 -- 線路、無線訊號、路由器等。-- 有助於用戶端電腦與伺服器電腦之間的通訊。 實體連線必須存在才能建立傳輸連線,而且必須建立傳輸連線,才能建立 SignalR 連線。 不過,中斷實體連線不一定會立即結束傳輸連線或 SignalR 連線,如本主題稍後所述。

在下圖中,SignalR 連線是由中樞 API 和 PersistentConnection API SignalR 層表示、傳輸連線是由傳輸層表示,而實體連線則以伺服器與用戶端之間的線條表示。

SignalR 架構圖表

當您在 SignalR 用戶端中呼叫 Start 方法時,您會提供 SignalR 用戶端程式代碼與伺服器建立實體連線所需的所有資訊。 SignalR 用戶端程式代碼會使用此資訊提出 HTTP 要求,並建立使用四種傳輸方法之一的實體連線。 如果傳輸連線失敗或伺服器失敗,則 SignalR 連線不會立即消失,因為用戶端仍具有自動重新建立相同 SignalR URL 的新傳輸連線所需的資訊。 在此案例中,不會涉及使用者應用程式的介入,而且當 SignalR 用戶端程式代碼建立新的傳輸連線時,它不會啟動新的 SignalR 連線。 SignalR 連線的持續性會反映在您呼叫 Start 方法時所建立的連接識別碼不會變更的事實。

OnReconnected 樞上的事件處理常式會在傳輸連線在遺失之後自動重新建立時執行。 OnDisconnected事件處理常式會在 SignalR 連線結束時執行。 SignalR 連線可以透過下列任何方式結束:

  • 如果用戶端呼叫 Stop 方法,則會將停止訊息傳送至伺服器,用戶端和伺服器都會立即結束 SignalR 連線。
  • 用戶端與伺服器之間的連線中斷之後,用戶端會嘗試重新連線,而伺服器會等候用戶端重新連線。 如果嘗試重新連線失敗,且中斷連線逾時期間結束,用戶端和伺服器都會結束 SignalR 連線。 用戶端會停止嘗試重新連線,而伺服器會處置其 SignalR 連線的標記法。
  • 如果用戶端停止執行而不有機會呼叫 Stop 方法,伺服器會等候用戶端重新連線,然後在中斷連線逾時期間之後結束 SignalR 連線。
  • 如果伺服器停止執行,用戶端會嘗試重新連線 (重新建立傳輸連線) ,然後在中斷連線逾時期間之後結束 SignalR 連線。

當沒有任何連線問題,而且使用者應用程式會呼叫 Stop 方法結束 SignalR 連線時,SignalR 連線和傳輸連線會同時開始和結束。 下列各節詳細說明其他案例。

傳輸中斷連線案例

實體連線可能會變慢,或連線中斷。 視中斷的長度等因素而定,可能會卸載傳輸連線。 SignalR 接著會嘗試重新建立傳輸連線。 有時候傳輸連線 API 會偵測中斷並卸載傳輸連線,而 SignalR 會立即發現連線遺失。 在其他案例中,傳輸連線 API 和 SignalR 都不會立即察覺到連線已遺失。 對於長時間輪詢以外的所有傳輸,SignalR 用戶端會使用稱為 keepalive 的函式來檢查傳輸 API 無法偵測到的連線遺失。 如需長時間輪詢連線的相關資訊,請參閱本主題稍後的 逾時和保留設定

當連線處於非使用中狀態時,伺服器會定期將保留封包傳送給用戶端。 自本文撰寫日期起,預設頻率每 10 秒。 藉由接聽這些封包,用戶端可以判斷是否有連線問題。 如果預期未收到保留封包,在短時間內,用戶端會假設有連線問題,例如緩慢或中斷。 如果保留在較長時間後仍然未收到,用戶端會假設已卸載連線,並開始嘗試重新連線。

下圖說明當傳輸 API 無法立即辨識實體連線發生問題時,在一般案例中引發的用戶端和伺服器事件。 此圖表適用于下列情況:

  • 傳輸是 WebSockets、永遠框架或伺服器傳送的事件。
  • 實體網路連線有各種不同的中斷期間。
  • 傳輸 API 不會察覺中斷,因此 SignalR 依賴保留功能來偵測它們。

傳輸中斷連線

如果用戶端進入重新連線模式,但無法在中斷連線逾時限制內建立傳輸連線,伺服器就會終止 SignalR 連線。 發生這種情況時,伺服器會執行中樞 OnDisconnected 的方法,並排入中斷連線訊息,以在用戶端管理稍後連線時傳送至用戶端。 如果用戶端接著重新連線,它會接收中斷連線命令,並呼叫 Stop 方法。 在此案例中, OnReconnected 用戶端重新連線時不會執行,而且 OnDisconnected 不會在用戶端呼叫 Stop 時執行。 下圖說明此案例。

傳輸中斷 - 伺服器逾時

用戶端上可能會引發的 SignalR 連線存留期事件如下:

  • ConnectionSlow client 事件。

    在收到最後一則訊息或保留 Ping 之後,已傳遞保留逾時期間的預設比例時引發。 預設的 keepalive 逾時警告期間為持續逾時的 2/3。 持續逾時為 20 秒,因此警告會在大約 13 秒發生。

    根據預設,伺服器每隔 10 秒傳送一次 keepalive ping,而用戶端會每隔 2 秒檢查一次保留 ping, (保留逾時值與保留逾時警告值之間的一分之一三差異) 。

    如果傳輸 API 發現中斷連線,SignalR 可能會在持續逾時警告期間通過之前收到中斷連線的通知。 在此情況下, ConnectionSlow 不會引發事件,而且 SignalR 會直接移至 Reconnecting 事件。

  • Reconnecting client 事件。

    當 (傳輸 API) 偵測到連線遺失時引發,或 (b) 自上次收到訊息或持續偵測之後已通過保留逾時期間。 SignalR 用戶端程式代碼會開始嘗試重新連線。 如果您想要讓應用程式在傳輸連線遺失時採取一些動作,可以處理此事件。 預設的保留逾時期間目前為 20 秒。

    如果您的用戶端程式代碼嘗試在 SignalR 處於重新連線模式時呼叫 Hub 方法,SignalR 會嘗試傳送命令。 在大部分情況下,這類嘗試將會失敗,但在某些情況下可能會成功。 針對伺服器傳送的事件、永遠框架和長時間輪詢傳輸,SignalR 會使用兩個通道,一個是用戶端用來傳送訊息,另一個用於接收訊息。 用於接收的通道是永久開啟的通道,而這是實體連線中斷時關閉的通道。 用於傳送的通道仍可供使用,因此如果還原實體連線,在重新建立接收通道之前,從用戶端到伺服器的方法呼叫可能會成功。 在 SignalR 重新開啟用於接收的通道之前,不會收到傳回值。

  • Reconnected client 事件。

    重新建立傳輸連線時引發。 中 OnReconnected 樞中的事件處理常式會執行。

  • Closed JavaScript 中的 client 事件 (disconnected 事件) 。

    當 SignalR 用戶端程式代碼在遺失傳輸連線之後嘗試重新連線時,中斷連線逾時期間到期時引發。 預設的中斷連線逾時為 30 秒。 (連線結束時也會引發這個事件,因為 Stop 呼叫 方法。)

傳輸連線中斷未由傳輸 API 偵測,且不會延遲從伺服器接收保留 Ping 的時間超過 keepalive 逾時警告期間,可能不會引發任何連線存留期事件。

某些網路環境刻意關閉閒置連線,而保留封包的另一個功能是讓這些網路知道 SignalR 連線正在使用中,以協助防止此情況。 在極端情況下,保留 Ping 的預設頻率可能不足以防止關閉的連線。 在此情況下,您可以設定更頻繁地傳送 keepalive Ping。 如需詳細資訊,請參閱本主題稍後的 逾時和保留設定

注意

重要

不保證此處所述的事件順序。 SignalR 會根據此配置,嘗試以可預測的方式引發連線存留期事件,但網路事件有許多變化,以及許多基礎通訊架構,例如傳輸 API 處理它們的方式。 例如, Reconnected 當用戶端重新連線時,可能不會引發事件,或者 OnConnected 當嘗試建立連線失敗時,伺服器上的處理常式可能會執行。 本主題只會描述通常由某些一般情況所產生的效果。

用戶端中斷連線案例

在瀏覽器用戶端中,維護 SignalR 連線的 SignalR 用戶端程式代碼會在網頁的 JavaScript 內容中執行。 這就是為什麼當您從一個頁面巡覽到另一個頁面時,SignalR 連線必須結束,這就是為什麼從多個瀏覽器視窗或索引標籤連線時有多個連線識別碼。 當使用者關閉瀏覽器視窗或索引標籤,或流覽至新的頁面或重新整理頁面時,SignalR 連線會立即結束,因為 SignalR 用戶端程式代碼會為您處理該瀏覽器事件並呼叫 Stop 方法。 在這些案例中,或在應用程式呼叫 Stop 方法時的任何用戶端平臺中,事件處理常式會在伺服器上立即執行, OnDisconnected 而用戶端會在 JavaScript) 中命名 disconnected 事件 (引發 Closed 事件。

例如,當使用者關閉膝上型電腦) 時,如果用戶端應用程式或其執行的電腦當機或進入睡眠 (,伺服器就不會收到發生什麼事的通知。 只要伺服器知道,用戶端遺失可能是因為連線中斷而用戶端可能嘗試重新連線。 因此,在這些案例中,伺服器會等候用戶端重新連線,而且 OnDisconnected 在中斷連線逾時期間到期之前不會執行,預設會 (大約 30 秒) 。 下圖說明此案例。

用戶端電腦失敗

伺服器中斷連線案例

當伺服器離線時,它會重新開機、失敗、應用程式網域回收等。-- 結果可能類似于遺失的連線,或傳輸 API 和 SignalR 可能立即知道伺服器已消失,而 SignalR 可能會開始嘗試重新連線而不引發 ConnectionSlow 事件。 如果用戶端進入重新連線模式,而且如果伺服器復原或重新開機或新伺服器在中斷連線逾時期間到期之前上線,用戶端將會重新連線到已還原或新的伺服器。 在此情況下,SignalR 連線會繼續在用戶端上,並 Reconnected 引發 事件。 在第一部伺服器上, OnDisconnected 永遠不會執行,而且在新伺服器上執行, OnReconnected 不過 OnConnected 該伺服器上從未針對該用戶端執行過。 (如果用戶端在重新開機或應用程式網域回收之後重新連線到相同的伺服器,效果會相同。因為當伺服器重新開機時,它沒有先前連線活動的記憶體。) 下圖假設傳輸 API 會立即察覺遺失的連接,因此 ConnectionSlow 不會引發事件。

伺服器失敗並重新連線

如果伺服器在中斷連線逾時期間內無法使用,SignalR 連線就會結束。 在此案例中, Closed JavaScript 用戶端中的事件 (disconnected 會在用戶端上引發) ,但 OnDisconnected 永遠不會在伺服器上呼叫。 下圖假設傳輸 API 不會察覺遺失的連線,因此會由 SignalR keepalive 功能偵測到,並 ConnectionSlow 引發事件。

伺服器失敗和逾時

逾時和保留設定

預設 ConnectionTimeoutDisconnectTimeoutKeepAlive 值適用于大部分案例,但如果您的環境有特殊需求,則可以變更。 例如,如果您的網路環境關閉閒置 5 秒的連線,您可能必須減少保留值。

ConnectionTimeout

此設定代表讓傳輸連線保持開啟並等待回應的時間量,再關閉並開啟新的連線。 預設值為 110 秒。

只有在停用保留功能時,才會套用此設定,這通常只適用于長時間輪詢傳輸。 下圖說明此設定對長時間輪詢傳輸連線的影響。

長時間輪詢傳輸連線

DisconnectTimeout

此設定代表在引發 Disconnected 事件之前,傳輸連線遺失之後等待的時間量。 預設值為 30 秒。 當您設定 DisconnectTimeout 時, KeepAlive 值會自動設定為 1/3 DisconnectTimeout

KeepAlive

此設定代表在透過閒置連線傳送保留封包之前要等候的時間量。 預設值為 10 秒。 此值不能超過 1/3 的值 DisconnectTimeout

如果您想要同時設定 DisconnectTimeoutKeepAlive ,請在 之後 DisconnectTimeout 設定 KeepAlive 。 否則,當逾時值自動設定 KeepAlive 為 1/3 時 DisconnectTimeout ,將會覆寫您的 KeepAlive 設定。

如果您想要停用 keepalive 功能,請將 設定 KeepAlive 為 null。 長時間輪詢傳輸會自動停用 Keepalive 功能。

如何變更逾時和保留設定

若要變更這些設定的預設值,請在Global.asax檔案中 Application_Start 設定預設值,如下列範例所示。 範例程式碼中顯示的值與預設值相同。

protected void Application_Start(object sender, EventArgs e)
{
    // Make long polling connections wait a maximum of 110 seconds for a
    // response. When that time expires, trigger a timeout command and
    // make the client reconnect.
    GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
    
    // Wait a maximum of 30 seconds after a transport connection is lost
    // before raising the Disconnected event to terminate the SignalR connection.
    GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
    
    // For transports other than long polling, send a keepalive packet every
    // 10 seconds. 
    // This value must be no more than 1/3 of the DisconnectTimeout value.
    GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
}

如何通知使用者中斷連線

在某些應用程式中,您可能會想要在發生連線問題時向使用者顯示訊息。 您有數個選項可用來執行這項操作。 下列程式碼範例適用于使用產生的 Proxy 的 JavaScript 用戶端。

  • connectionSlow處理事件,以在 SignalR 知道連線問題後立即顯示訊息,再進入重新連線模式。

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • reconnecting處理事件,以在 SignalR 知道中斷連線並進入重新連線模式時顯示訊息。

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • disconnected處理事件,以在嘗試重新連線時顯示訊息。在此案例中,重新建立與伺服器的連線的唯一方式是呼叫 Start 方法來重新開機 SignalR 連線,這會建立新的連線識別碼。 下列程式碼範例會使用 旗標,以確保您只在重新連線逾時之後發出通知,而不是呼叫 Stop 方法所造成的一般 SignalR 連線結束之後發出通知。

    var tryingToReconnect = false;
    
    $.connection.hub.reconnecting(function() {
        tryingToReconnect = true;
    });
    
    $.connection.hub.reconnected(function() {
        tryingToReconnect = false;
    });
    
    $.connection.hub.disconnected(function() {
        if(tryingToReconnect) {
            notifyUserOfDisconnect(); // Your function to notify user.
        }
    });
    

如何持續重新連線

在某些應用程式中,您可能會想要在連線遺失後自動重新建立連線,而且嘗試重新連線已逾時。若要這樣做,您可以從事件處理常式呼叫 Start 方法 Closeddisconnected (JavaScript 用戶端上的事件處理常式) 。 您可能會想要等候一段時間,再呼叫 Start ,以避免在伺服器或實體連線無法使用時太頻繁地執行此動作。 下列程式碼範例適用于使用產生的 Proxy 的 JavaScript 用戶端。

$.connection.hub.disconnected(function() {
   setTimeout(function() {
       $.connection.hub.start();
   }, 5000); // Restart connection after 5 seconds.
});

在行動用戶端中要注意的潛在問題是,當伺服器或實體連線無法使用時,持續重新連線嘗試可能會導致不必要的電池耗盡。

如何在伺服器程式碼中中斷用戶端連線

SignalR 1.1.1 版沒有用於中斷用戶端連線的內建伺服器 API。 未來有 計畫新增這項功能。 在目前的 SignalR 版本中,將用戶端與伺服器中斷連線的最簡單方式是在用戶端上實作中斷連線方法,並從伺服器呼叫該方法。 下列程式碼範例示範使用產生的 Proxy 之 JavaScript 用戶端的中斷連線方法。

var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

警告

安全性 - 此方法無法中斷用戶端連線,也不會解決建議的內建 API,都會解決執行惡意程式碼的遭入侵用戶端案例,因為用戶端可以重新連線或駭客程式碼可能會移除 stopClient 方法,或變更其用途。 實作具狀態拒絕服務的適當位置 (DOS) 保護不在架構或伺服器層中,而是在前端基礎結構中。