2013-02-25 12 views
8

Ho due applicazioni semplici:C# rilevare tcp disconnessione

  • un'applicazione server che attende su una porta TCP specifica per un client di connettersi. Quindi ascolta ciò che dice, invia un feedback e DISCONNETTI quel cliente.

  • Un'applicazione Modulo che si collega all'applicazione server, quindi dice qualcosa, quindi attende il feedback e disconnette dal server, quindi mostra il feedback nel modulo.

Anche se l'applicazione server sembra comportarsi correttamente (ho provato con Telnet e vedo il feedback e vedo lo scollamento che si verificano subito dopo il feedback), l'applicazione modulo tuttavia non sembra accorgersi la disconnessione dal server. (TcpClient.Connected sembra rimanere vero anche dopo che il server si è disconnesso)

La mia domanda è: perché TcpClient.Connected rimane fedele e come posso sapere se/quando il server è disconnesso?

Ecco il mio codice completo: applicazione

Forma: applicazione

using System; 
using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System.Windows.Forms; 
using System.Threading; 

namespace Sender 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void sendButton_Click(object sender, EventArgs e) 
     { 
      TcpClient tcpClient = new TcpClient(); 
      tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 81); 
      responseLabel.Text = "waiting for response..."; 
      responseLabel.Invalidate(); 

      // write request 
      NetworkStream networkStream = tcpClient.GetStream(); 
      byte[] buffer = (new ASCIIEncoding()).GetBytes("Hello World! "); 
      networkStream.Write(buffer, 0, buffer.Length); 
      networkStream.Flush(); 

      // read response 
      Thread readThread = new Thread(new ParameterizedThreadStart(ReadResponse)); 
      readThread.Start(tcpClient); 
     } 

     void ReadResponse(object arg) 
     { 
      TcpClient tcpClient = (TcpClient)arg; 
      StringBuilder stringBuilder = new StringBuilder(); 
      NetworkStream networkStream = tcpClient.GetStream(); 
      bool timeout = false; 
      DateTime lastActivity = DateTime.Now; 
      while (tcpClient.Connected && !timeout) 
      { 
       if (networkStream.DataAvailable) 
       { 
        lastActivity = DateTime.Now; 
        while (networkStream.DataAvailable) 
        { 
         byte[] incomingBuffer = new byte[1024]; 
         networkStream.Read(incomingBuffer, 0, 1024); 
         char[] receivedChars = new char[1024]; 
         (new ASCIIEncoding()).GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0); 
         stringBuilder.Append(receivedChars); 
        } 
       } 
       else 
       { 
        if (DateTime.Now > lastActivity.AddSeconds(60)) 
         timeout = true; 
       } 
       System.Threading.Thread.Sleep(50); 
      } 
      Invoke((MethodInvoker)delegate 
      { 
       responseLabel.Text = "Response from Listener:\n" + stringBuilder.ToString(); 
       responseLabel.Invalidate(); 
      }); 

      if (timeout) 
      { 
       Console.Write("A timeout occured\n"); 
       networkStream.Close(); 
       tcpClient.Close(); 
      } 
     } 

    } 
} 

Server:

using System.Net; 
using System.Net.Sockets; 
using System.Text; 
using System; 
using System.Threading; 

namespace Listener 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var tcpListener = new TcpListener(IPAddress.Any, 81); 
      tcpListener.Start(); 
      Thread clientThread = new Thread(new ParameterizedThreadStart(Listen)); 
      clientThread.Start(tcpListener); 
     } 

     static void Listen(object arg) 
     { 
      TcpListener tcpListener = (TcpListener)arg; 
      while (true) 
      { 
       TcpClient tcpClient = tcpListener.AcceptTcpClient(); 
       Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClient)); 
       clientThread.Start(tcpClient); 
      } 
     } 

     static void HandleClient(object arg) 
     { 
      TcpClient tcpClient = (TcpClient)arg; 
      StringBuilder stringBuilder = new StringBuilder(); 
      ASCIIEncoding encoder = new ASCIIEncoding(); 
      DateTime lastActivity = DateTime.Now; 

      // read request 
      NetworkStream networkStream = tcpClient.GetStream(); 
      int timeout = 5; // gives client some time to send data after connecting 
      while (DateTime.Now < lastActivity.AddSeconds(timeout) && stringBuilder.Length==0) 
      { 
       if (!networkStream.DataAvailable) 
       { 
        System.Threading.Thread.Sleep(50); 
       } 
       else 
       { 
        while (networkStream.DataAvailable) 
        { 
         lastActivity = DateTime.Now; 
         byte[] incomingBuffer = new byte[1024]; 
         networkStream.Read(incomingBuffer, 0, 1024); 
         char[] receivedChars = new char[1024]; 
         encoder.GetDecoder().GetChars(incomingBuffer, 0, 1024, receivedChars, 0); 
         stringBuilder.Append(receivedChars); 
        } 
       } 
      } 
      string request = stringBuilder.ToString(); 

      // write response 
      string response = "The listener just received: " + request; 
      byte[] outgoingBuffer = encoder.GetBytes(response); 
      networkStream.Write(outgoingBuffer, 0, outgoingBuffer.Length); 
      networkStream.Flush(); 

      networkStream.Close(); 
      tcpClient.Close(); 
     } 
    } 

} 

risposta

9

TcpClient/NetworkStream non viene notificato quando la connessione è chiusa. L'unica opzione disponibile consiste nel rilevare le eccezioni durante la scrittura nello stream.

Alcuni anni fa ci siamo spostati a utilizzare socket invece di client tcp. socket è più utilizzabile rispetto a tcpclient.

ci sono un paio di metodi che è possibile utilizzare

sondaggio è uno di loro

http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.poll.aspx

Si può anche fare un controllo sul risultato di scrittura stessa. ti dà il numero di byte effettivamente scritto.

La proprietà Connected riflette solo lo stato dell'ultima operazione. La documentazione indica "Il valore della proprietà Connected riflette lo stato della connessione come dell'ultima operazione. Se è necessario determinare lo stato corrente della connessione, effettuare una chiamata di invio a zero byte non bloccante. restituisce correttamente o genera un codice di errore WAEWOULDBLOCK (10035), quindi il socket è ancora connesso, altrimenti il ​​socket non è più connesso. "

http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx

+0

concordato, socket sarebbe una soluzione molto migliore da usare qui – XikiryoX

+0

NetworkStream stesso utilizza un socket (anche se non è un bene pubblico). Come potrei essere in grado di usarlo? In altre parole: se stavo usando Socket, come procedo a sapere se il client è disconnesso? –

+0

Dai un'occhiata a Poll .. Sono d'accordo che Network Stream e TcpClient usano Socket ma forniscono un'interfaccia di alto livello .. Socket comunque è socket .. quello che vedi è quello che ottieni :) –

0
networkStream.DataAvailable 

Questa proprietà non notifica se il socket viene chiuso sul lato server.

Nella vostra situazione, è possibile implementare una funzione "keepalive" in cui si esegue il polling della connessione ogni t minuti/secondi, o aggiungere un sacco di try/catch ogni volta che si tenta di leggere o scrivere dal socket. Quando uso TCP, includo solo un metodo Reconnect() che entra in ogni dichiarazione catch.

Problemi correlati