以下はASP.Net CoreとMicrosoft.AspNetCore.SignalRで作ったチャットの基礎になるアプリです。接続すると全員に接続した旨と時刻とConnectionIdを表示し、切断した場合も同様の表示をします。チャット機能は省略しています。

単純に接続と切断だけを通知するサンプルアプリ

Program.cs

Pages\Index.cshtml.cs

Pages\Index.cshtml

これで[接続]ボタンや[切断]ボタンをクリックすると以下のように表示されます。またブラウザを閉じた場合も切断ボタンをクリックしたときと同じように動作します。

サーバー上ではブラウザを閉じた直後にOnDisconnectedAsyncが実行されない

これで完成♪のはずなのですが、これをサーバー上で動作させてみると問題点があることに気づきします。[切断]ボタンをクリックしたときはすぐに「切断 XX:XX:XX XXXXXXXXXXXXXXXXXXXXXX」と表示されるのですが、ブラウザを閉じた場合は切断された表示がされるまでかなりの時間がかかるのです(ローカルでテストした場合はこの問題は発生しない)。

改善案

これを解決する方法として以下があります(ベストプラクティスかどうかわからないのですが……)。

接続したら接続しているあいだ断続的にサーバーサイドからクライアントサイドにデータを送り続け、これに失敗したら切断されたと判断します。この方法だとブラウザを閉じたときもすぐに結果が反映されます。

通信が切れたことを知る

あと通信が切れた場合、クライアントサイドで気づくことができるようにします。scriptタグ内に以下を追加します。

Pages\Index.cshtml

自動的に再接続する

なんらかの理由で切断されてしまった場合、再接続するのであれば以下のようにします。[一時的に切断(自動再接続)]ボタンをクリックした場合は一時的に切断されますが、3秒後に再接続を試みます。もし再接続できない場合はあきらめます。

Pages\Index.cshtml

再接続者の同一性の確認

再接続した場合、ConnectionIdも変わってしまいます。再接続できた場合、それが同一者であることを確認する手段を用意しなければなりません。

一例として接続に成功したらクライアントサイドにConnectionIdを送信します。クライアントサイドではこれを保存しておきます。再接続に成功したら新しいConnectionIdが送信されますが、このとき古いConnectionIdと新しいConnectionIdをサーバーサイドに送り返すことで同一性が認識できます。

OnConnectedAsyncメソッド内に1行追加します。そしてクライアントサイドからSendIdToServerが呼び出されるので、そのメソッドを定義します。それ以外の変更点はありません。

Pages\Index.cshtml

グローバル変数 oldConnectionIdとnewConnectionIdを用意します。function connect()を修正するとともに、サーバーサイドから”SendIdToClient”が送信されたときの処理を追加します。