2016-03-18 8 views
4

現在、私は単純なクライアントサーバーチャットプログラム(C#のクライアントサーバー通信の事に入りたい)に取り組んでいます。これは、サーバーから正しく切断されていない場合を除き、これまでのところ動作します。私は、接続を閉じるために、ここで、このコードを使用するクライアントでTcpClientとNetworkStreamを閉じる際の問題

:クライアントからのメッセージを待っているスレッド化ループが存在しているサーバー上

client.Client.Disconnect(false); // client is the TcpClient 
client.Close(); 

private void StartChat() 
{ 
    int requestCount = 0; 
    byte[] bytesFrom = new byte[10025]; 
    string dataFromClient = null; 
    string rCount = null; 

    while (true) 
    { 
     try 
     { 
      requestCount++; 

      NetworkStream stream = tcpClient.GetStream(); 

      int bufferSize = (int)tcpClient.ReceiveBufferSize; 
      if (bufferSize > bytesFrom.Length) 
      { 
       bufferSize = bytesFrom.Length; 
      } 

      stream.Read(bytesFrom, 0, bufferSize); 
      dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom); 
      dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$")); 
      rCount = Convert.ToString(requestCount); 

      string message = client.Name + " says: " + dataFromClient; 
      program.Broadcast(message); 

     } 
     catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException) 
     { 
      program.UserDisconnected(client); 
      break; 
     } 
     catch(ArgumentOutOfRangeException ex) 
     { 
      Debug.WriteLine(ex.ToString()); 
      break; 
     } 
     catch(Exception ex) 
     { 
      Debug.WriteLine(ex.ToString()); 
      break; 
     } 
    } 

クライアントが接続を切断します上記のコードでは、関数は常にストリームをフェッチして、そのような出力を生成しています。

\0\0\0\0\0\0\0 [and so on]

$のインデックスがないため、この場合はArgumentOutOfRangeExceptionがスローされます。私はループを避けて無限に実行するためにbreakを追加しました。

驚いたことに、ObjectDisposedExceptionは投げられません。また、ストリームが閉じられて接続が拒否されたため、System.IO.IOExceptionはスローされません。

サーバに接続されているクライアントアプリケーションを閉じるだけでは、サーバはループを停止せず、クライアントが切断されたためにストリームが到着するのを待っています。

クライアントの接続が切断されたかどうかを検出するにはどうしたらいいですか?そして、私は接続を閉じるために適切な方法をクライアント接続を閉じる方法ですか?

ありがとうございました!

更新:

private void StartChat() 
{ 
    int requestCount = 0; 
    byte[] bytesFrom = new byte[10025]; 
    string dataFromClient = null; 
    string rCount = null; 

    while (true) 
    { 
     try 
     { 
      requestCount++; 

      NetworkStream stream = tcpClient.GetStream(); 
      stream.ReadTimeout = 4000; 

      int bufferSize = (int)tcpClient.ReceiveBufferSize; 
      if (bufferSize > bytesFrom.Length) 
      { 
       bufferSize = bytesFrom.Length; 
      } 


      // Wait for a client message. If no message is recieved within the ReadTimeout a IOException will be thrown 
      try 
      { 
       int bytesRead = stream.Read(bytesFrom, 0, bufferSize); 
       stream.Flush(); 

       if (bytesRead == 0) 
       { 
        throw new System.IO.IOException("Connection seems to be refused or closed."); 
       } 
      } 
      catch (System.IO.IOException) 
      { 
       byte[] ping = System.Text.Encoding.UTF8.GetBytes("%"); 
       stream.WriteTimeout = 1; 

       stream.Write(ping, 0, ping.Length); 
       continue; 
      } 


      dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom); 
      dataFromClient = dataFromClient.Substring(0, dataFromClient.IndexOf("$")); 
      rCount = Convert.ToString(requestCount); 

      string message = client.Name + " says: " + dataFromClient; 
      program.Broadcast(message); 

     } 
     catch(Exception ex) when (ex is ObjectDisposedException || ex is InvalidOperationException || ex is System.IO.IOException) 
     { 
      Debug.WriteLine(ex.ToString()); 
      program.UserDisconnected(client); 
      break; 
     } 
     catch(ArgumentOutOfRangeException ex) 
     { 
      Debug.WriteLine(ex.ToString()); 
     } 
     catch(Exception ex) 
     { 
      Debug.WriteLine(ex.ToString()); 
      break; 
     } 
    } 
} 

答えて

3

あなたはstream.Readの戻り値をチェックする必要があります - それは実際に読み取りバイトの数を返します。

クライアントが切断されたときには0になり、データを読み取るときには読み取られたバイト数がバッファサイズよりも小さいことがよくあります。最近のコメントに応えて

int bytes = stream.Read(bytesFrom, 0, bufferSize); 
if (bytes == 0) 
{ 
    // client has disconnected 
    break; 
} 

dataFromClient = System.Text.Encoding.UTF8.GetString(bytesFrom, 0, bytes); 

、クライアントが接続を終了したときに三つのことが起こることができます。

  • をクライアントが正しく接続を閉じ、そしてあなたは0バイト
  • の検出が起こった何かを受け、
  • クライアントは「終了」していますが、エンドに信号が送信されません。

この最後の状況では、明らかに失敗するデータを送信しようとするまで、サーバはアクティブな接続があるかのように動作します。これは、多くのプロトコルが接続タイムアウトおよび/またはキープアライブメカニズムで動作する理由です。

+0

これは、クライアントがなくなったことを検出できないことを意味しますか?ドキュメントを見るとObjectDispoedExceptionまたは少なくともIOExceptionがスローされるはずです – chris579

+0

_graceful_ disconnectは例外ではなく、0を返す 'stream.Read()'によって検出されます。例外ハンドラを持っていますが、自分で処理してから使用しようとしない限り、 'ObjectDisposedException'がスローされることはありません。 –

+0

そうですが、このオブジェクトがまだ存在し、ガベージコレクタによって削除されていないか手動で処理されていない限り、DisposedExceptionはスローされません。しかし、巨大な感謝、これは働いた!私はあなたの答えをできるだけ早く受け入れます。 編集:クライアントを閉じると、ループは終了せず、サーバーアプリケーションが終了するまで保持されます。どうすればこの問題を解決できますか? – chris579

1

int bytesRead = stream.Read(...); if(bytesRead == 0)//クライアントが切断された

+0

あなたはゆっくりします。 )上記の答えを見てください;) – chris579

+0

ya ...ここには十分な開発者がいることは知っています。おそらく、サイトは新しいコメント/回答があったときに知らせることができます。 :p – ABuckau

+0

ええ:)まだ最初の答えで私のコメントを見てみようとしている1つの問題があります。多分あなたはこれで私を助けることができます;) – chris579

関連する問題