2016-03-18 5 views
4

zur Zeit arbeite ich an einem einfachen Client-Server-Chat-Programm (wollte in Client-Server Kommunikation Dinge in C#). Es funktioniert soweit bis auf die ordnungsgemäße Trennung vom Server.Probleme mit dem Schließen von TcpClient und NetworkStream

Auf dem Client ich hier diesen Code verwenden, um die Verbindung zu schließen:

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

Auf dem Server eine Gewindeschleife dort für Nachrichten vom Client wartet:

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; 
     } 
    } 

Wenn der Client die Verbindung trennt Mit dem oben gezeigten Code holt die Funktion den Strom die ganze Zeit und erzeugt eine solche Ausgabe:

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

In diesem Fall wird die ArgumentOutOfRangeException ausgelöst, da es keinen Index von $ gibt. Ich habe eine break hinzugefügt, um die endlose Ausführung der Schleife zu vermeiden.

Überraschenderweise wird die ObjectDisposedException nicht geworfen werden. Auch die System.IO.IOException wird nicht geworfen, aber es sollte, weil der Stream geschlossen wurde, so dass die Verbindung verweigert wurde.

Wenn ich die Client-Anwendung nur schließen, die mit dem Server verbunden ist, der Server die Schleife nicht beendet, es wartet nur für einen Strom, der nie kommen wird, weil der Client getrennt.

Wie kann ich feststellen, ob der Client nicht mehr verbunden ist oder nicht mehr erreichbar ist? Und wie schließe ich die Client-Verbindung ordnungsgemäß, um eine Verbindung zu schließen?

Danke für Ihre Hilfe!

Update:

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; 
     } 
    } 
} 

Antwort

3

Sie sollten den Rückgabewert von stream.Read überprüfen - es gibt die Anzahl der Bytes tatsächlich lesen.

Dies wird 0 sein, wenn die Client-Verbindung beendet hat, und wenn er Daten gelesen hat, lesen Sie die Anzahl der Bytes sind oft weniger als die Puffergröße.

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

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

Als Antwort auf Ihren letzten Kommentar, können drei Dinge passieren, wenn ein Client die Verbindung beendet:

  • Der Client korrekt die Verbindung schließt, und Sie erhalten 0 Byte
  • Etwas nachweisbar passiert ist, eine Ausnahme verursacht
  • Der Kunde ist „verschwunden“, aber es wird kein Signal zu Ihrem Ende
  • gesendet

In dieser letzten Situation verhält sich der Server immer noch so, als wäre eine aktive Verbindung, bis er versucht, Daten zu senden, die offensichtlich fehlschlagen würden. Aus diesem Grund arbeiten viele Protokolle mit einem Verbindungs-Timeout und/oder einem Keep-Alive-Mechanismus.

+0

Bedeutet dies, dass Sie nicht feststellen können, ob der Client gegangen ist? Beim Betrachten der Doku sollte es eine ObjectDispoedException oder zumindest eine IOException auslösen, weil es nicht mehr davon lesen kann. – chris579

+0

Ein _graceful_ disconnect ist kein Ausnahmefall und wird von 'stream.Read()' gefunden, das 0 zurückgibt habe einen Exception-Handler, aber ich bezweifle, dass "ObjectDisposedException" ausgelöst wird, es sei denn, du entwirfst es selbst und versuchst es dann zu benutzen. –

+0

Sie haben Recht, DisposedException wird nur ausgelöst, wenn dieses Objekt noch vorhanden ist und nicht vom Garbage Collector gelöscht oder manuell entfernt wurde. Aber, großer Dank, das hat funktioniert! Ich werde Ihre Antwort so schnell wie möglich akzeptieren. Edit: Wenn ich den Client schließe, wird die Schleife nicht beendet und bleibt, bis die Serveranwendung geschlossen wurde. Wie könnte ich das beheben? – chris579

1

int BytesRead = stream.Read (...); if (bytesRead == 0) // Client getrennt

+0

Nun, du bist zu langsam. Sehen Sie sich die Antwort oben an;) – chris579

+0

ya ... Ich weiß, dass es hier genug Entwickler gibt, vielleicht könnte die Seite eine Person wissen lassen, wenn es einen neuen Kommentar/eine neue Antwort gibt ... und die Seitennummerierung korrigieren, während sie gerade dabei sind : p – ABuckau

+0

Yeah :) Es ist immer noch ein Problem ausstehend nur auf meinen Kommentar in der ersten Antwort. Vielleicht kannst du mir dabei helfen;) – chris579

Verwandte Themen