2009-09-02 7 views
8

私はC#でクライアントを1つだけ受け入れることができるサーバーを開発しています。このクライアントが切断されたときに別の接続要求を受け入れることができるようにする必要があります。.NETでクライアントの切断を検出するためのベストプラクティスはありますか。

私は接続要求を継続的に聞き取り、Socket.BeginAcceptでクライアントを受け入れるか拒否する最初のソケットを使用しています。クライアントが受け入れられると、Socket.EndAcceptによって返される新しいソケットは、クライアントとサーバー間の通信に使用されます。次に、サーバーはクライアントからのコマンドをSocket.Begin/EndReceiveで待機し、応答を送信します。サーバーはTelnetのようなプロトコルを使用します。つまり、各コマンドと各応答行は、\r\nで終わらなければなりません。

クライアントが切断されているかどうかを検出するために、空のメッセージ( "\r\n")をクライアントに500msごとに送信するタイマーをインストールしました。クライアントが切断されると、Socketによって例外がスローされます。この例外は、現在のセッションを閉じて新しい接続を受け入れるサーバーによって捕捉されます。このソリューションは堅牢ですが、ネットワーク上の不要なトラフィックを意味し、実際の応答を得る前にダミーメッセージをフィルタリングする必要があるクライアントによって正しく処理されなければなりません。

空のバッファー(Socket.Send(new byte[1], 0, 0))を送信しようとしましたが、方向サーバー - >クライアントでは機能しないようです。

もう1つの解決策は、Socket.EndReceiveが0バイトを返す場合を処理することです。 「アイドル」時間中に発生する断線の場合には問題ありません。しかし、メッセージ転送中にクライアントが切断された場合、サーバは必ずしもそれを見ず、無期限に待機します。

私はすでにこの問題に関するいくつかのスレッドと質問を見てきましたが、私は決して良い解決策を見たことがありません。

私の質問は:どのような切断の検出に最適な方法は何ですか?ネット。

答えて

4

唯一の他の選択肢は、TCPがTCPをキープアライブごとに頻繁に送信することです。これは今現在行っていることのようにポーリングしていますが、TCPレイヤーで処理されているのでプロトコルはありません知っている必要はありません。

しかし、他のクライアントに何かを送信せずに応答を受け取ると、まだ接続されているかどうかを知る方法がないので、ポーリングの周りには方法はありません。

標準のNAPTなどのステートフルパケットインスペクションを介して通信を行う場合は、リモートサーバがインアクティビティによってセッションを切断しないようにする必要があります。

+0

OK、私のソケットにKeepAliveを有効にするコードを追加しました。しかし、私は今何を期待していますか?クライアントが接続を切断したときに何も起こりません...私は例外をどこかで受け取ることを期待していました...定期的に何かを読んだり書いたりするべきですか? – cedrou

+1

Begin/EndReceiveを使用していましたが?その場合、BeginReceiveはコールバックを実行する必要があります。結果を取得するためにEndReceiveを呼び出すときには、例外が発生するか、ストリームの最後が0バイト読み出されます。 –

+0

OK、今例外が発生しました...ありがとう – cedrou

3

以下の方法を使用して、クライアントがまだ接続されているかどうかを調べることができます。この

public static bool IsConnected(this TcpClient client) 
{ 
    try 
    { 
     bool connected = !(client.Client.Poll(1, SelectMode.SelectRead) && client.Client.Available == 0); 

     return connected; 
    } 
    catch 
    { 
     return false; 
    } 
} 

このanswerは、私は私のコードスニペットを得た方法でテストソケットのためのものです。

+2

このメソッドは、クライアントが接続を明示的に閉じた場合はうまく動作しますが、接続が切断された場合(ケーブルが接続されていない場合など)は正常に動作しません。 – cedrou

+0

どこが生き続けるかは、OSとTCPプロトコルがクライアントへの定期的なメッセージを処理して生きているかどうかを調べ、ソケットをポーリングしてローカルの状態をチェックします。キープアライブが失敗した場合、ソケットは接続されなくなったことを報告する必要があります。 –

+0

非同期のBegin/EndReceiveを使用すると、ソケットがキープアライブに失敗したときにエラーでコールバックを受け取る必要があるため、ポーリングは必要ありません。 –

0

クライアントを制御できますか?もしそうなら、あなたはクライアントが切断していることを示す特別なパケットをサーバーに送り、ソケットを切断するだけでよいのですか?

クライアントが実際に切断された(Socket.Shutdown()またはSocket.Close()を使用して)ケースを検出しようとしていますか、またはクライアントが長時間アイドル状態にあるかどうかを検出しようとしていますか?排出される?

どちらの場合でも、クライアントは定期的なメッセージをサーバーに送信させるようにしてください。サーバーは最後のハートビートを追跡することができ、クライアントがハートビート数を3回以上逃した場合、クライアントを切断できます。はい、余分なデータが含まれていますが、それは物事の壮大なスキームでそれほど悪くはありません。十分な期間にハートビートを送信し、クライアントが生存しているかどうかを知ることができ、ハートビートの間に時間がかからないようにチューニングすれば、非常に優れたシステムを構築できます。

関連する問題