【C#】TCP/IPサーバで通信の切断を検出する


TCP/IPサーバでクライアントと通信している時、クライアントとの接続が切れた場合の処理のまとめです。
接続が切れたとは、以下の場合です。

  • クライアントが回線を切った
  • クライアントが強制終了した
  • ケーブルが外れるなど通信経路が切れた

クライアントが接続を切った

クライアントがshutdownを実行して回線を切った場合、サーバーのNetworkStream.Read()は長さ0の通信を受け取って終了します。

normal_end
    int readsize = stream.Read( buf, 0, buf.Length );
    if( readsize == 0 )
    {
        // クライアントが切断したときの処理
    }

クライアントが強制終了した

クライアントがWSACleanupを実行たり、ExitProcessして回線を切った場合、サーバーのNetworkStream.Read()は例外を投げて終了します。

forced_end
    try
    {
        int readsize = stream.Read( buf, 0, buf.Length );
        // クライアントからの受信処理
    }
    catch( Exception e )
    {
        // クライアントが切断したときの処理
    }

ケーブルが外れるなど通信経路が切れた

通常は無反応です。
Keep-aliveの設定を行っておくと設定条件となった時、強制終了したときと同様にNetworkStream.Read()は例外を投げて終了します。

keep-aliveとは、接続している相手に定期的にプローブという通信を投げ応答があるかどうかを確認するもので、応答がなくなったら回線に異常が生じたと検出する手法です。
プローブのON/OFF、クライアントとの通信がなくなってからプローブを送信するまでの待ち時間、プローブを送信する間隔を設定します。

keep-alive_setting
    var clientSocket = listenerSocket.Accept();
    // keep-aliveをenableに設定
    clientSocket.SetSocketOption( SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true );

    // keep-aliveのパラメータ Cで言うところの、unsigned int が3つです。
    byte[] tcp_keepalive = new byte[12];
    BitConverter.GetBytes( (Int32)1 ).CopyTo( tcp_keepalive, 0 );//onoffスイッチ.
    BitConverter.GetBytes( (Int32)2000 ).CopyTo( tcp_keepalive, 4 );//wait time.(ms)
    BitConverter.GetBytes( (Int32)1000 ).CopyTo( tcp_keepalive, 8 );//interval.(ms)
    // keep-aliveのパラメータ設定
    clientSocket.IOControl( IOControlCode.KeepAliveValues, tcp_keepalive, null );

待ち時間と間隔はミリ秒で設定します。
この例では、スイッチはON、待ち時間2秒、間隔1秒、です。Windowsでは10回連続でプローブの応答がなかったら例外を発生するようです。

プローブはネットワークトラフィックを増やします。その点は注意。

参考URL
https://www.codeproject.com/Articles/117557/Set-Keep-Alive-Values