6

TcpListener を使用しているときに「クライアントの切断」を検出する方法を探していました。

すべての答えは次のようなものです: TcpListener: クライアントの切断を検出するにはどうすればよいですか?

基本的に、ストリームから読み取り、Read() が 0 を返す場合、クライアントは切断されています。

ただし、これは、クライアントがデータ ストリームを送信するたびに切断することを前提としています。私たちは、TCP の接続/切断のオーバーヘッドが遅く高価な環境で運用しています。

接続を確立してから、多数のリクエストを送信します。

擬似コード:

client.Connect();
client.GetStatus();
client.DoSomething();
client.DoSomethingElse();
client.AndSoOn();
client.Disconnect();

Connect と Disconnect() の間の各呼び出しは、データのストリームをサーバーに送信します。サーバーは、ストリームを分析および処理する方法を知っています。

切断せずに TcpListener をループで読み取らせた場合、すべてのメッセージを読み取って処理しますが、クライアントが切断された後、サーバーはそれを知る方法がなく、クライアントを解放して新しいメッセージを受け入れることはありません。

var read = client.GetStream().Read(buffer, 0, buffer.Length);

if (read > 0) 
{
   //Process
}

read == 0 のときに TcpListener にクライアントをドロップさせると、データの最初のストリームのみを受け入れ、直後にクライアントをドロップします。

もちろん、これは新しいクライアントが接続できることを意味します。

呼び出し間に人為的な遅延はありませんが、コンピューター時間に関しては、2 つの呼び出し間の時間はもちろん「巨大」であるため、クライアントが持っている、またはすべきではないことを意味しなくても、常に read == 0 の時間が存在します。切断されます。

var read = client.GetStream().Read(buffer, 0, buffer.Length);

if (read > 0) 
{
   //Process
}
else
{
   break;   //Always executed as soon as the first stream of data has been received
}

だから私は疑問に思っています...クライアントが切断されたかどうかを検出するより良い方法はありますか?

4

3 に答える 3

3

エレンの答えは私の問題を解決しました。他の誰かが同じ問題に直面している場合に備えて、Socket.Receive メソッドを使用した「サンプル」コードを次に示します。

private void AcceptClientAndProcess()
{
    try
    {
        client = server.Accept();
        client.ReceiveTimeout = 20000;
    }
    catch
    {
        return;
    }

    while (true)
    {
        byte[] buffer = new byte[client.ReceiveBufferSize];
        int read = 0;

        try
        {
            read = client.Receive(buffer);
        }
        catch
        {
            break;
        }

        if (read > 0)
        {
            //Handle data
        }
        else
        {
            break;
        }
    }

    if (client != null)
        client.Close(5000);
}

どこかのループで AcceptClientAndProcess() を呼び出します。

次の行:

read = client.Receive(buffer);

どちらかになるまでブロックします

  • データが受信された (読み取り > 0) この場合、それを処理できます
  • 接続は適切に閉じられました (読み取り = 0)
  • 接続が突然閉じられました (例外がスローされます)

最後の 2 つの状況のいずれかは、クライアントが接続されていないことを示しています。

Socket.Accept() メソッドの周りの try catch も必要です。これは、接続フェーズ中にクライアント接続が突然閉じられた場合に失敗する可能性があるためです。

読み取り操作に 20 秒のタイムアウトが指定されていることに注意してください。

于 2012-07-26T07:01:59.000 に答える
2

NetworkStream.Socketプロパティを使用して基になるソケットを取得し、そのReceiveメソッドを使用して読み取りを行うことができます。

とは異なりNetworkStream.Read、 のリンクされたオーバーロードはSocket.Receive、指定されたバイト数が読み取られるまでブロックされ、リモート ホストが TCP 接続をシャットダウンした場合にのみ 0 を返します。


更新: @jrh のコメントは正しく、これNetworkStream.Socketは保護されたプロパティであり、このコンテキストではアクセスできません。client を取得するには、新しく確立された接続に対応するオブジェクトを返すTcpListener.AcceptSocketメソッドをSocket使用できます。Socket

于 2012-07-26T05:15:53.133 に答える
1

のドキュメントにNetworkStream.Readはこれが反映されていませんが、私の経験では、ポートがまだ開いていてデータが利用できない場合、「NetworkStream.Read」はブロックされますが、ポートが閉じられている場合は 0 が返されます。

反対側からこの問題に遭遇しましNetworkStream.Readた。現在利用可能なデータがない場合、すぐに 0 が返されません。現在データを読み取れるNetworkStream.DataAvailableかどうかを調べるために使用する必要があります。NetworkStream.Read

于 2013-11-04T14:42:10.990 に答える