2015-12-24 1 views
2

J'ai essayé d'obtenir que mon application serveur affiche un message dans une étiquette lorsque mon client s'est déconnecté.Message affiché lorsque TCPClient se déconnecte

Actuellement, l'étiquette affiche l'adresse IP du client connecté lors du démarrage du client, mais lorsque le client est arrêté, l'adresse IP est toujours affichée dans l'étiquette.

J'ai essayé ces méthodes jusqu'à présent sans aucune chance:

  // DETECT IF CLIENT DISCONNECTED 

//METHOD 1 
      if (bytesRead == 0) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
       break; 
      } 

//METHOD 2 
      if (!tcpListener.Pending()) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      } 

//METHOD 3 
      if (tcpClient.Client.Poll(0, SelectMode.SelectRead)) 
      { 
       byte[] buff = new byte[1]; 
       if (tcpClient.Client.Receive(buff, SocketFlags.Peek) == 0) 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }     

Je suis sûr que c'est probablement quelque chose simple que je suis absent, je ne peux pas comprendre ce qu'il pourrait être.


EDIT: Je l'ai trouvé un peu d'une solution où lorsque je clique sur le bouton qui ferme mon application client, le client envoie un message au serveur avant la fermeture. Le serveur sait que s'il reçoit ce message particulier (dans ce cas, "SHUTDOWN CLIENT") pour changer le texte dans "ClientIPLabel.text" à "(No Clients Connected)"

Cela fonctionne pour la plupart, mais c'est une sorte de piratage, et si le client a fermé à cause d'une erreur ou d'un crash, etc. Le serveur ne sait pas qu'il s'est déconnecté, donc il affichera toujours la dernière adresse IP connue qu'il a été envoyé.

2nd EDIT: Il semble donc que cette solution de contournement ne fonctionnera pas pour moi. Après avoir ajouté cela à mon projet, pour quelque raison que ce soit, quel que soit le message que mon client envoie au serveur, le message "(No Clients Connected)" s'affiche.

Mon client est toujours connecté en fait, et recevoir des messages, mais le ClientIPLabel 'est pas étiqueté correctement



3 EDIT: Voici un extrait montrant comment j'ai obtenu mon client configuré pour envoyer des messages au serveur selon détaillé dans un de mes commentaires ci-dessous:

private void SendMessage(string msg) 
    { 
     NetworkStream clientStream = ConsoleClient.GetStream(); 

     ASCIIEncoding encoder = new ASCIIEncoding(); 
     byte[] buffer = encoder.GetBytes(msg); 

     clientStream.Write(buffer, 0, buffer.Length); 
     clientStream.Flush(); 
    } 

4 EDIT: Mon code serveur, avec ma solution temporaire à obtenir un message de déconnexion du client directement à partir du bouton d'arrêt du client:

public partial class Server : Form 
{ 
    private TcpListener tcpListener; 
    private Thread listenThread;  
    private delegate void WriteMessageDelegate(string msg); 

    public Server() 
    { 
     InitializeComponent(); 
     Server(); 
    } 


    // WAIT FOR CLIENT 

    private void Server() 
    { 
     this.tcpListener = new TcpListener(IPAddress.Any, 8888); 
     this.listenThread = new Thread(new ThreadStart(ListenForClients)); 
     this.listenThread.Start(); 
    } 

    private void ListenForClients() 
    { 
     this.tcpListener.Start(); 


    // GET CLIENT IP ADDRESS 

     while (true) 
     { 
      TcpClient client = this.tcpListener.AcceptTcpClient();     
      string clientIPAddress = "" + IPAddress.Parse(((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString()); 
      ClientIPLabel.Text = clientIPAddress; 
      Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm)); 
      clientThread.Start(client);    
     } 
    } 


    // COMMUNICATE WITH CLIENT 

    private void HandleClientComm(object client) 
    { 
     TcpClient tcpClient = (TcpClient)client; 
     NetworkStream clientStream = tcpClient.GetStream(); 

     byte[] message = new byte[4096]; 
     int bytesRead; 

     while (true) 
     { 
      bytesRead = 0; 

      try 
      { 
       bytesRead = clientStream.Read(message, 0, 4096); 
      } 
      catch 
      { 
       break; 
      } 


      ASCIIEncoding encoder = new ASCIIEncoding(); 


    // CHECK FOR CLIENT DISCONNECT 

      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     } 

     tcpClient.Close(); 
    } 


5 EDIT: J'ai mis à jour mon code, et j'ai (presque) eu le chronomètre de pulsation mis en œuvre, mais il n'est toujours pas configuré tout à fait raison ... Voici mon code:

// CHECK FOR CLIENT DISCONNECT 

      // SHUTDOWN BUTTON PRESSED 
      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     }       
     tcpClient.Close(); 
    } 

    // CHECK FOR CONNECTION FAILED 
    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      HandleClientComm("Check for connection"); 
     } 
     catch (Exception) 
     {     
      Invoke((MethodInvoker)delegate 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 

Le message "(No Clients Connected)" s'affiche automatiquement sur mon serveur après la minuterie, que mon client se soit déconnecté ou non, de sorte qu'il n'attrape pas correctement l'exception.

J'ai essayé de mettre en œuvre la suggestion interceptwind comme il l'écrit, mais pour une raison quelconque où je devrais avoir catch (Exception e) Je ne suis en mesure de construire si je me débarrasser de la e et l'écrire comme catch (Exception). Si j'y laisse e, j'obtiens un avertissement disant "The variable 'e' is declared but never used".

De plus, je sais qu'interswind a écrit son exemple pour utiliser ma méthode SendMessage(), mais cette méthode n'est utilisée que dans mon client, donc j'ai changé le code pour essayer d'utiliser ma méthode HandleClientComm(), donc je me demande si c'est la raison pour laquelle cela ne fonctionne pas correctement. J'ai essayé de changer quelques choses, mais je n'arrive toujours pas à le faire fonctionner. Après 5 secondes, je reçois toujours le message "(No Clients Connected)" même si mon client est toujours connecté et fonctionne correctement.


6 EDIT: Je ai essayé ajuster ma méthode HandleClientComm() pour pouvoir envoyer des messages, mais je suis quelque chose de toute évidence manqué, parce que mon « minuterie de rythme cardiaque » commute toujours mon ClientIPLabel.text à " (Aucun client connecté) "même si mon client est toujours connecté.

Voici mon code:

// COMMUNICATE WITH CLIENT 

    private void HandleClientComm(object client) 
    { 
     TcpClient tcpClient = (TcpClient)client; 
     NetworkStream clientStream = tcpClient.GetStream(); 
     ASCIIEncoding encoder = new ASCIIEncoding(); 
     byte[] message = new byte[4096]; 
     byte[] buffer = encoder.GetBytes("Send Message"); 
     int bytesRead; 
     clientStream.Write(buffer, 0, buffer.Length); 
     clientStream.Flush(); 
     while (true) 
     { 
      bytesRead = 0; 

      try 
      { 
       bytesRead = clientStream.Read(message, 0, 4096); 
      } 
      catch 
      { 
       break; 
      }         


      // START HEARTBEAT TIMER 

      System.Timers.Timer heartbeatTimer = new System.Timers.Timer(); 
      heartbeatTimer.Interval = 5000; 
      heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
      heartbeatTimer.Start(); 

      // CHECK FOR CLIENT DISCONNECT 

      // SHUTDOWN BUTTON PRESSED 
      string msg = encoder.GetString(message, 0, bytesRead); 
      WriteMessage(msg); 

      if (msg.Equals("Client Disconnected (" + DateTime.Now + ")")) 
      { 
       ClientIPLabel.Text = ("(No Client Connected)"); 
      }    
     }       
     tcpClient.Close(); 
    } 

    // CHECK FOR CONNECTION FAILED 
    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      HandleClientComm("Check for connection"); 
     } 
     catch (Exception) 
     {     
      Invoke((MethodInvoker)delegate 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 
+0

Votre application serveur a une interface graphique? –

+0

Oui, il se trouve juste dans la barre d'état système, mais il a également une interface graphique de base que vous pouvez ouvrir pour afficher les messages que mon client envoie au serveur (comme un journal), mais aussi l'adresse IP du client connecté au bas de l'interface graphique. – Patrick

+0

L'objet que vous utilisez pour écouter le client a-t-il un événement tel que "OnDisconnected" ou un événement que vous pouvez surveiller? –

Répondre

1

Compte tenu de la restriction de l'OP que le serveur ne peut pas envoyer un message au client, l'autre est pour le client d'envoyer des messages heartbeat au serveur toutes les X secondes (par exemple 5 secondes). Le serveur vérifiera alors s'il a reçu un message du client au cours des dernières secondes Y (par exemple, 30 secondes). Si ce n'est pas le cas, cela signifie que le client est déconnecté.

Code des clients

public Client() 
    { 
     ... 

     //After connection, CALL ONCE ONLY 
     Timer heartbeatTimer = new System.Timers.Timer(); 
     heartbeatTimer.Interval = 5000; //5 seconds 
     heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
     heartbeatTimer.Start(); 
    } 

    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     SendMessage("Heartbeat"); 
    } 

Code

de serveur
bool receiveHeartBeat = false; 

    public Server() 
    { 
     ... 

     //After connection, CALL ONCE ONLY 
     Timer checkHeartbeatTimer = new System.Timers.Timer(); 
     checkHeartbeatTimer.Interval = 30000; //30 seconds 
     checkHeartbeatTimer.Elapsed += checkHeartbeatTimer_Elapsed; 
     checkHeartbeatTimer.Start(); 
    } 

    void checkHeartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     if(receiveHeartBeat) 
     { 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "Connected"; 
      }); 
     } 
     else 
     { 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 

    void WriteMessage(string msg) 
    { 
     receiveHeartBeat = true; 
     // rest of your code 

    } 
+0

Merci de m'avoir fait beaucoup d'efforts pour m'aider. Je pense que je vais faire une pause pendant un moment, et utiliser ma petite solution de contournement pour l'instant; pour quelque raison que ce soit cette chose ne cesse de me passer par la tête ... J'ai encore d'autres parties de mon projet à travailler qui ne me donnent pas tant de mal lol. – Patrick

+1

Je vais marquer cela comme la bonne réponse parce que je sais que c'est. Je ne peux pas encore le faire fonctionner, mais c'est sur mon niveau de compétence, pas sur votre réponse. – Patrick

1

Vous devez probablement utiliser:

if (TcpClient.Connected){} 
+0

Le "== vrai" est redondant/inutile. –

+0

malheureusement, cela ne fonctionne pas pour moi non plus – Patrick

1

commentaire de Will est correct. Vous devez probablement implémenter un mécanisme Heartbeat, dans lequel vous envoyez périodiquement un message Heartbeat (ou factice) à votre client. Si vous obtenez une erreur lorsque vous essayez d'envoyer Heartbeat, vous savez que votre client a été déconnecté.

La façon la plus simple à mettre en œuvre ce ressemblera à quelque chose comme ceci:

//using System.Timers; 

    private void ListenForClients() 
    { 
     this.tcpListener.Start(); 

     Timer heartbeatTimer = new System.Timers.Timer(); 
     heartbeatTimer.Interval = 5000; //5 seconds 
     heartbeatTimer.Elapsed += heartbeatTimer_Elapsed; 
     heartbeatTimer.Start(); 

     //rest of your code 
    } 


    void heartbeatTimer_Elapsed(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      SendMessage("Can be anything :D"); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine(e.Message); //Check what is the exception 

      //Fail to send message - client disconnected. 
      Invoke((MethodInvoker)delegate //prevent cross-thread exception 
      { 
       ClientIPLabel.Text = "(No Clients Connected)"; 
      }); 
     } 
    } 
+0

Awesome! On dirait que ça va marcher! Je n'ai pas encore eu l'occasion de le tester, mais j'avais une question sur une partie de celle-ci. Quand il envoie le message "HEARTBEAT", il semble qu'il soit envoyé en tant que chaîne "msg", est-ce correct? Je demande seulement, parce que la partie principale de mon application repose sur mon client envoyant déjà des messages au serveur comme "msg", ainsi je pourrais voir cela en conflit avec la fonction principale de mon application. Si ce n'est pas le cas, alors c'est génial. Sinon, suis-je capable de changer "msg" en quelque chose d'autre, comme "message" ou quelque chose comme ça? Ou aurai-je besoin de m'inquiéter à ce sujet? – Patrick

+0

J'ai mis à jour ma question pour inclure le bit de code où mon client envoie déjà des messages au serveur – Patrick

+0

Ughhh .... Je déteste être si nouveau à cela. J'ai essayé d'ajuster votre code pour l'adapter à différents endroits de mon projet, et je n'arrive pas à le faire fonctionner correctement. J'ai mis à jour ma question avec mon code de serveur si quelqu'un veut m'aider à utiliser le "mécanisme de pulsation". Désolé d'être si noobish ... – Patrick