SignalR 1.x での接続有効期間イベントの理解と処理

作成者: Patrick FletcherTom Dykstra

警告

このドキュメントは、SignalR の最新バージョン用ではありません。 SignalR の ASP.NET Coreを見てみましょう。

この記事では、処理できる SignalR 接続、再接続、切断イベント、および構成できるタイムアウトとキープアライブ設定の概要について説明します。

この記事では、SignalR と接続の有効期間イベントに関する知識が既にあるものとします。 SignalR の概要については、「SignalR - 概要 - はじめに」を参照してください。 接続の有効期間イベントの一覧については、次のリソースを参照してください。

概要

この記事は、次のセクションで構成されています。

API リファレンス トピックへのリンクは、API の .NET 4.5 バージョンに関するページです。 .NET 4 を使用している場合は、 .NET 4 バージョンの API トピックを参照してください。

接続の有効期間の用語とシナリオ

SignalR Hub のイベント ハンドラーは OnReconnected 、特定のクライアントの直後 OnConnected に実行できますが、 の後 OnDisconnected には実行できません。 切断せずに再接続できる理由は、SignalR で "connection" という単語を使用する方法がいくつかあります。

SignalR 接続、トランスポート接続、および物理接続

この記事では、 SignalR 接続トランスポート接続物理接続を区別します。

  • SignalR 接続とは、SignalR API によって管理され、接続 ID によって一意に識別される、クライアントとサーバー URL の間の論理関係を指します。 このリレーションシップに関するデータは SignalR によって管理され、トランスポート接続を確立するために使用されます。 リレーションシップが終了し、SignalR がメソッドを呼び出 Stop すとき、または SignalR が失われたトランスポート接続の再確立を試みている間にタイムアウト制限に達すると、SignalR によってデータが破棄されます。
  • トランスポート接続 とは、クライアントとサーバーの間の論理関係を指し、WebSocket、サーバー送信イベント、永久フレーム、または長いポーリングの 4 つのトランスポート API のいずれかによって維持されます。 SignalR はトランスポート API を使用してトランスポート接続を作成し、トランスポート API はトランスポート接続を作成するための物理ネットワーク接続の存在に依存します。 トランスポート接続は、SignalR が終了したとき、またはトランスポート API が物理接続が切断されたことを検出したときに終了します。
  • 物理接続 とは、ワイヤ、ワイヤレス信号、ルーターなどの物理ネットワーク リンクを指します。-- クライアント コンピューターとサーバー コンピューター間の通信を容易にします。 トランスポート接続を確立するには物理接続が存在する必要があり、SignalR 接続を確立するにはトランスポート接続を確立する必要があります。 ただし、物理接続を切断しても、トランスポート接続または SignalR 接続がすぐに終了するとは限りません。これについては、このトピックで後述します。

次の図では、SignalR 接続は Hubs API と PersistentConnection API SignalR レイヤーで表され、トランスポート接続は Transports レイヤーで表され、物理接続はサーバーとクライアントの間の行で表されます。

SignalR アーキテクチャの図

SignalR クライアントで メソッドを Start 呼び出すときは、サーバーへの物理的な接続を確立するために必要なすべての情報を SignalR クライアント コードに提供します。 SignalR クライアント コードでは、この情報を使用して HTTP 要求を行い、4 つのトランスポート方法のいずれかを使用する物理接続を確立します。 トランスポート接続が失敗した場合、またはサーバーが失敗した場合、クライアントには同じ SignalR URL への新しいトランスポート接続を自動的に再確立するために必要な情報が残っているため、SignalR 接続はすぐには切断されません。 このシナリオでは、ユーザー アプリケーションからの介入は必要ありません。SignalR クライアント コードが新しいトランスポート接続を確立しても、新しい SignalR 接続は開始されません。 SignalR 接続の継続性は、メソッドを呼び出したときに作成される接続 ID が変更されないという事実に Start 反映されます。

ハブ上のイベント ハンドラーは OnReconnected 、失われた後にトランスポート接続が自動的に再確立されたときに実行されます。 イベント ハンドラーは OnDisconnected 、SignalR 接続の最後に実行されます。 SignalR 接続は、次のいずれかの方法で終了できます。

  • クライアントが メソッドを Stop 呼び出すと、サーバーに停止メッセージが送信され、クライアントとサーバーの両方が SignalR 接続を直ちに終了します。
  • クライアントとサーバー間の接続が失われた後、クライアントは再接続を試み、サーバーはクライアントの再接続を待機します。 再接続の試行が失敗し、切断タイムアウト期間が終了すると、クライアントとサーバーの両方が SignalR 接続を終了します。 クライアントは再接続の試行を停止し、サーバーは SignalR 接続の表現を破棄します。
  • メソッドを呼び出 Stop さずにクライアントの実行が停止した場合、サーバーはクライアントの再接続を待機し、切断タイムアウト期間後に SignalR 接続を終了します。
  • サーバーの実行が停止した場合、クライアントは再接続 (トランスポート接続の再作成) を試み、切断タイムアウト期間後に SignalR 接続を終了します。

接続の問題がなく、ユーザー アプリケーションが メソッドを呼び出して SignalR 接続を Stop 終了すると、SignalR 接続とトランスポート接続が約同時に開始および終了します。 以降のセクションでは、他のシナリオについて詳しく説明します。

トランスポート切断のシナリオ

物理接続が遅いか、接続が中断される可能性があります。 中断の長さなどの要因によっては、トランスポート接続が切断される可能性があります。 その後、SignalR はトランスポート接続の再確立を試みます。 トランスポート接続 API によって中断が検出され、トランスポート接続が切断されることがあり、SignalR は接続が失われることを直ちに検出します。 その他のシナリオでは、トランスポート接続 API も SignalR も、接続が失われたことをすぐに認識しません。 長いポーリングを除くすべてのトランスポートについて、SignalR クライアントは keepalive という関数を使用して、トランスポート API が検出できない接続の損失をチェックします。 長いポーリング接続の詳細については、このトピックで後述する 「タイムアウトとキープアライブ設定 」を参照してください。

接続が非アクティブな場合、サーバーは定期的にキープアライブ パケットをクライアントに送信します。 この記事の執筆時点では、既定の頻度は 10 秒ごとです。 これらのパケットをリッスンすることで、クライアントは接続の問題があるかどうかを確認できます。 予期した場合にキープアライブ パケットが受信されない場合、しばらくすると、クライアントは低速や中断などの接続の問題があると見なします。 長い時間が経過してもキープアライブが受信されない場合、クライアントは接続が切断されたと見なし、再接続の試行を開始します。

次の図は、トランスポート API によってすぐに認識されない物理接続に問題がある場合に、一般的なシナリオで発生するクライアントイベントとサーバー イベントを示しています。 この図は、次の状況に適用されます。

  • トランスポートは、WebSocket、永久フレーム、またはサーバー送信イベントです。
  • 物理ネットワーク接続には、さまざまな期間の中断があります。
  • トランスポート API は中断を認識しないため、SignalR はキープアライブ機能に依存してそれらを検出します。

トランスポートの切断

クライアントが再接続モードに入っても、切断タイムアウト制限内でトランスポート接続を確立できない場合、サーバーは SignalR 接続を終了します。 その場合、サーバーはハブの OnDisconnected メソッドを実行し、クライアントが後で接続を管理する場合に備えて、切断メッセージをキューに入れ、クライアントに送信します。 クライアントは再接続すると、disconnect コマンドを受け取り、 メソッドを Stop 呼び出します。 このシナリオでは、 OnReconnected はクライアントが再接続するときに実行されず、 OnDisconnected クライアントが を呼び出 Stopしたときには実行されません。 次の図は、このシナリオを示しています。

トランスポートの中断 - サーバーのタイムアウト

クライアントで発生する可能性がある SignalR 接続の有効期間イベントは次のとおりです。

  • ConnectionSlow クライアント イベント。

    最後のメッセージまたはキープアライブ ping が受信されてから、キープアライブ タイムアウト期間のプリセットの割合が経過したときに発生します。 既定のキープアライブ タイムアウト警告期間は、キープアライブ タイムアウトの 2/3 です。 キープアライブ タイムアウトは 20 秒であるため、警告は約 13 秒で発生します。

    既定では、サーバーは 10 秒ごとにキープアライブ ping を送信し、クライアントは約 2 秒ごとにキープアライブ ping をチェックします (キープアライブ タイムアウト値とキープアライブ タイムアウト警告値の差の 3 分の 1)。

    トランスポート API が切断を認識した場合、キープアライブ タイムアウト警告期間が経過する前に、SignalR に切断が通知される可能性があります。 その場合、イベントは ConnectionSlow 発生せず、SignalR はイベントに直接移動します Reconnecting

  • Reconnecting クライアント イベント。

    (a) トランスポート API が接続が失われることを検出したとき、または (b) 最後のメッセージまたはキープアライブ ping が受信されてからキープアライブ タイムアウト期間が経過したときに発生します。 SignalR クライアント コードが再接続の試行を開始します。 トランスポート接続が失われたときにアプリケーションで何らかのアクションを実行する場合は、このイベントを処理できます。 既定のキープアライブ タイムアウト期間は、現在 20 秒です。

    SignalR が再接続モードの間にクライアント コードがハブ メソッドを呼び出そうとすると、SignalR はコマンドの送信を試みます。 ほとんどの場合、このような試行は失敗しますが、状況によっては成功する可能性があります。 サーバー送信イベント (永続的フレーム、および長いポーリング トランスポート) の場合、SignalR は 2 つの通信チャネルを使用します。1 つはクライアントがメッセージの送信に使用し、もう 1 つはメッセージの受信に使用します。 受信に使用されるチャネルは永続的に開いているチャネルであり、物理接続が中断されたときに閉じられるチャネルです。 送信に使用されるチャネルは引き続き使用可能であるため、物理接続が復元された場合、受信チャネルが再確立される前にクライアントからサーバーへのメソッド呼び出しが成功する可能性があります。 SignalR が受信に使用するチャネルを再度開くまで、戻り値は受信されません。

  • Reconnected クライアント イベント。

    トランスポート接続が再確立されたときに発生します。 OnReconnectedハブ内のイベント ハンドラーが実行されます。

  • Closed client イベント (disconnected JavaScript のイベント)。

    SignalR クライアント コードがトランスポート接続を失った後に再接続しようとしている間に、切断タイムアウト期間が経過したときに発生します。 既定の切断タイムアウトは 30 秒です。 (このイベントは、 メソッドが呼び出されるため Stop 、接続が終了したときにも発生します)。

トランスポート API によって検出されず、キープアライブ タイムアウト警告期間を超えてサーバーからのキープアライブ ping の受信を遅延しないトランスポート接続の中断は、接続の有効期間イベントが発生しない可能性があります。

一部のネットワーク環境では、意図的にアイドル状態の接続が閉じられ、キープアライブ パケットのもう 1 つの機能は、SignalR 接続が使用されていることをこれらのネットワークに知らせることでこれを防ぐことです。 極端なケースでは、キープアライブ ping の既定の頻度では、閉じた接続を防ぐには不十分な場合があります。 その場合は、キープアライブ ping をより頻繁に送信するように構成できます。 詳細については、このトピックで後述する 「タイムアウトとキープアライブ設定 」を参照してください。

Note

重要

ここで説明する一連のイベントは保証されません。 SignalR は、このスキームに従って予測可能な方法で接続の有効期間イベントを発生させるすべての試行を行いますが、ネットワーク イベントにはさまざまなバリエーションがあり、トランスポート API などの基になる通信フレームワークで処理される方法は多数あります。 たとえば、クライアントが Reconnected 再接続したときにイベントが発生しない場合や OnConnected 、接続の確立が失敗したときにサーバー上のハンドラーが実行される場合があります。 このトピックでは、特定の一般的な状況によって通常生成される効果について説明します。

クライアント切断のシナリオ

ブラウザー クライアントでは、SignalR 接続を維持する SignalR クライアント コードは、Web ページの JavaScript コンテキストで実行されます。 そのため、あるページから別のページに移動するときに SignalR 接続を終了する必要があります。そのため、複数のブラウザー ウィンドウまたはタブから接続する場合は、複数の接続 ID を持つ複数の接続があります。 ユーザーがブラウザー ウィンドウまたはタブを閉じたり、新しいページに移動したり、ページを更新したりすると、SignalR クライアント コードがそのブラウザー イベントを処理して メソッドを Stop 呼び出すので、SignalR 接続はすぐに終了します。 これらのシナリオでは、またはアプリケーションが メソッドを呼び出 Stop すときに任意のクライアント プラットフォームで、 OnDisconnected イベント ハンドラーがサーバー上で直ちに実行され、クライアントによってイベントが発生します Closed (イベントは JavaScript で名前が付けられます disconnected )。

クライアント アプリケーションまたは実行中のコンピューターがクラッシュしたり、スリープ状態になったりした場合 (たとえば、ユーザーがノート PC を閉じるとき)、サーバーには何が発生したかが通知されません。 サーバーが認識している限り、クライアントの損失は接続の中断が原因であり、クライアントが再接続しようとしている可能性があります。 したがって、これらのシナリオでは、サーバーはクライアントに再接続の機会を与えるのを待ち OnDisconnected 、切断タイムアウト期間が経過するまで実行されません (既定では約 30 秒)。 次の図は、このシナリオを示しています。

クライアント コンピューターのエラー

サーバーの切断シナリオ

サーバーがオフラインになると、再起動、失敗、アプリ ドメインのリサイクルなどが行われます。-- 結果は、接続が失われた場合と似ているか、トランスポート API と SignalR がサーバーがなくなったことをすぐに認識し、SignalR がイベントを ConnectionSlow 発生させることなく再接続を開始する可能性があります。 クライアントが再接続モードになり、サーバーが回復または再起動した場合、または切断タイムアウト期間が経過する前に新しいサーバーがオンラインになった場合、クライアントは復元されたサーバーまたは新しいサーバーに再接続します。 その場合、SignalR 接続はクライアントで続行され、 Reconnected イベントが発生します。 最初のサーバーでは、 OnDisconnected は実行されません。新しいサーバーでは が実行されますがOnConnectedOnReconnectedそのサーバー上でそのクライアントに対して実行されることはありませんでした。 (再起動またはアプリ ドメインのリサイクル後にクライアントが同じサーバーに再接続した場合、サーバーの再起動時に以前の接続アクティビティのメモリがないため、効果は同じです)。次の図では、トランスポート API が失われた接続をすぐに認識し、イベントが ConnectionSlow 発生しないことを前提としています。

サーバーの障害と再接続

切断タイムアウト期間内にサーバーが使用できなくなった場合、SignalR 接続は終了します。 このシナリオでは、 Closed イベント (disconnected JavaScript クライアントの場合) はクライアントで発生しますが、 OnDisconnected サーバーでは呼び出されません。 次の図は、トランスポート API が失われた接続を認識しないため、SignalR キープアライブ機能によって検出され、 ConnectionSlow イベントが発生することを前提としています。

サーバーの障害とタイムアウト

タイムアウトとキープアライブ設定

既定 ConnectionTimeoutの 、、 DisconnectTimeoutおよび KeepAlive の値は、ほとんどのシナリオに適していますが、環境に特別なニーズがある場合は変更できます。 たとえば、ネットワーク環境がアイドル状態の接続を 5 秒間閉じる場合、キープアライブ値を減らす必要がある場合があります。

ConnectionTimeout

この設定は、トランスポート接続を開いたままにし、応答を待機してから応答を閉じて新しい接続を開く時間を表します。 既定値は 110 秒です。

この設定は、キープアライブ機能が無効になっている場合にのみ適用されます。通常は、長いポーリング トランスポートにのみ適用されます。 次の図は、この設定が長いポーリング トランスポート接続に及ぼす影響を示しています。

長いポーリング トランスポート接続

DisconnectTimeout

この設定は、イベントを発生させる前にトランスポート接続が失われた後に待機する時間を表します Disconnected 。 既定値は 30 秒です。 を設定DisconnectTimeoutKeepAliveすると、値の 1/3 に自動的に設定されますDisconnectTimeout

KeepAlive

この設定は、アイドル状態の接続を介してキープアライブ パケットを送信するまでの待機時間を表します。 既定値は 10 秒です。 この値は、値の DisconnectTimeout 1/3 を超えてはなりません。

と の両方DisconnectTimeoutを設定する場合は、 の後に DisconnectTimeoutを設定KeepAliveKeepAliveします。 それ以外の場合KeepAlive、タイムアウト値の 1/3 に自動的に設定KeepAliveされるとDisconnectTimeout、設定が上書きされます。

キープアライブ機能を無効にする場合は、null に設定 KeepAlive します。 キープアライブ機能は、長いポーリング トランスポートに対して自動的に無効になります。

タイムアウトとキープアライブ設定を変更する方法

これらの設定の既定値を変更するには、次の例に Application_Start 示すように、 Global.asax ファイルで設定します。 サンプル コードに示されている値は、既定値と同じです。

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);
}

切断についてユーザーに通知する方法

一部のアプリケーションでは、接続の問題が発生したときにユーザーにメッセージを表示することが必要な場合があります。 これを行う方法とタイミングには、いくつかのオプションがあります。 次のコード サンプルは、生成されたプロキシを使用する JavaScript クライアント用です。

  • イベントを connectionSlow 処理して、SignalR が接続の問題を認識するとすぐに、再接続モードになる前にメッセージを表示します。

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • SignalR が切断を reconnecting 認識し、再接続モードになったときにメッセージを表示するイベントを処理します。

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • イベントを disconnected 処理して、再接続の試行がタイムアウトしたときにメッセージを表示します。このシナリオでは、サーバーとの接続を再度確立する唯一の方法は、 メソッドを呼び出して SignalR 接続を Start 再起動することです。これにより、新しい接続 ID が作成されます。 次のコード サンプルでは、 フラグを使用して、メソッドの呼び出しによって発生する SignalR 接続が正常に終了した後ではなく、再接続タイムアウト後にのみ通知を Stop 発行することを確認します。

    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.
        }
    });
    

継続的に再接続する方法

一部のアプリケーションでは、接続が失われ、再接続の試行がタイムアウトした後に、接続を自動的に再確立することが必要になる場合があります。これを行うには、イベント ハンドラー (disconnectedJavaScript クライアントのイベント ハンドラー) から Closed メソッドを呼び出Startすことができます。 サーバーまたは物理接続が使用できないときに頻繁に行われるのを避けるために、呼び出す Start 前に一定の時間待つ必要がある場合があります。 次のコード サンプルは、生成されたプロキシを使用する JavaScript クライアント用です。

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

モバイル クライアントで注意すべき潜在的な問題は、サーバーまたは物理接続が使用できない場合に継続的な再接続が試行されると、不要なバッテリのドレインが発生する可能性があるということです。

サーバー コードでクライアントを切断する方法

SignalR バージョン 1.1.1 には、クライアントを切断するための組み込みのサーバー API がありません。 今後、この機能を追加する予定があります。 現在の SignalR リリースでは、クライアントをサーバーから切断する最も簡単な方法は、クライアントに disconnect メソッドを実装し、サーバーからそのメソッドを呼び出す方法です。 次のコード サンプルは、生成されたプロキシを使用する JavaScript クライアントの切断方法を示しています。

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

警告

セキュリティ - クライアントを切断するためのこの方法も、提案された組み込み API も、クライアントが再接続したり、ハッキングされたコードがメソッドを削除 stopClient したり、実行内容を変更したりする可能性があるため、悪意のあるコードを実行しているハッキングされたクライアントのシナリオには対処しません。 ステートフルサービス拒否 (DOS) 保護を実装するための適切な場所は、フレームワークまたはサーバー層ではなく、フロントエンド インフラストラクチャにあります。