2016-02-02 1 views
3

Je travaille sur ce qui devrait être une simple application serveur TCP qui reçoit des données d'un flux et les stocke dans une liste.Le serveur TCP ne reçoit pas correctement lorsque les données sont dynamiquement saisies

J'ai réussi à obtenir les données quand elles sont envoyées dans un message par connexion. Mon problème semble être lors du test en utilisant telnet à partir de la ligne de commande. Je vais commencer à taper et le programme va saisir un ou deux caractères en fonction de ma vitesse de frappe et ne recevra plus rien de ce flux à nouveau. Je suis vraiment à court de pourquoi. Si je mets un thread.sleep dans la boucle stream.DataAvailable, il aura quelques caractères de plus mais s'arrêtera de nouveau. Toute aide est appréciée. Ma classe est la suivante:

public class TCPServer 
{ 
    private TcpListener listener; 

    public List<string> messages; 

    public TCPServer(IPAddress ip, int port) 
    { 
     try 
     { 
      messages = new List<string>(); 

      listener = new TcpListener(ip, port); 
      listener.Start(); 

      listener.BeginAcceptTcpClient(ClientConnected, listener); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
     } 
    } 

    private void ClientConnected(IAsyncResult ar) 
    { 

     TcpClient client; 
     try 
     { 
      client = listener.EndAcceptTcpClient(ar); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
      return; 
     } 

     try 
     { 
      NetworkStream stream = client.GetStream(); 
      string message = ""; 

      while (!stream.DataAvailable) 
      { 
      } 

      while (stream.DataAvailable) 
      {     
       byte[] readBytes = new byte[65536]; 
       stream.Read(readBytes, 0, readBytes.Length); 
       message += Encoding.ASCII.GetString(readBytes).TrimEnd('\0'); 
      } 

      if (message != null) 
      { 
       messages.Add(message); 
       message = ""; 
      } 
      listener.BeginAcceptTcpClient(ClientConnected, listener); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
     } 
    } 

    public void Stop() 
    { 
     listener.Stop(); 
    } 

} 

Répondre

0

J'ai donc réussi à résoudre ce problème en regardant plus dans la classe NetworkStream. Dans le callback de BeginReceiveTcpClient, j'ai extrait le flux réseau du tcpclient pour pouvoir accéder à la méthode asynchrone BeginReceive. Le code pertinent est ci-dessous:

private void ClientConnected(IAsyncResult ar) 
    { 
     byte[] readBytes = new byte[65536]; 

     TcpClient client; 
     try 
     { 
      client = listener.EndAcceptTcpClient(ar); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
      return; 
     } 

     try 
     { 
      NetworkStream stream = client.GetStream(); 
      stream.BeginRead(buffer, 0, buffer.Length, ReadStream, stream); 

      listener.BeginAcceptTcpClient(ClientConnected, listener); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
     } 
    } 

    private void ReadStream(IAsyncResult ar) 
    { 
     try 
     { 

      NetworkStream stream = (NetworkStream)ar.AsyncState; 
      int bytesRead = stream.EndRead(ar); 

      content += Encoding.ASCII.GetString(buffer, 0, bytesRead); 
      dataProcessor.ProcessData(messages); 
      stream.BeginRead(buffer, 0, buffer.Length, ReadStream, stream); 

     } 
     catch (Exception ex) 
     { 
      Console.WriteLine("Error: " + ex.Message); 
     } 
    } 
2

La boucle de lecture est très incorrecte.

Vous ne comprenez pas que DataAvailable ne vous dit pas combien d'octets vont arriver. Il vous donne une limite inférieure et pourrait dire 0 à tout moment. La boucle s'annulera tôt. L'utilisation de DataAvailable est presque toujours incorrecte.

Cela ne mène à rien et est une attente active qui devrait soulever un drapeau rouge avec vous:

 while (!stream.DataAvailable) 
     { 
     } 

La bonne façon de lire est d'appeler simplement Read. Vous devez utiliser la valeur de retour de Read pour voir combien d'octets sont arrivés. Le rognage des zéros est incorrect (vous ne pouvez pas recevoir de zéros de cette façon) et un hack qui, encore une fois, devrait déclencher un drapeau rouge mental! Cela ne peut pas être la bonne façon de le faire.

Les prises sont très difficiles à obtenir. Ne faites pas de hacks suspects, cela augmente seulement le risque d'échecs non-déterministes.

+0

Si ce n'est pas la bonne façon de le faire (ce que je crois) pouvez-vous montrer la bonne façon? Cela n'a pas vraiment répondu à la question. – Resistance

+0

Eh bien, ça répond pourquoi ça ne marche pas. Vous devriez probablement essayer de mettre en place une boucle de lecture. Si votre protocole est basé sur une ligne, vous pouvez essayer StreamReader.ReadLine. Rechercher sur le Web un tutoriel de socket (qui ne contient pas le mot DataAvailable :)). Il n'est pas nécessaire de répéter ce contenu ici. – usr

+0

DataAvailable est un booléen dans NetworkStreams, pas une valeur entière, usr. Donc, vérifier si c'est vrai ou faux fonctionnera certainement. L'OP ne l'a même jamais utilisé dans le contexte de la vérification du nombre d'octets à recevoir. Il existe une différence entre 'Socket.DataAvailable' et' NetworkStream.DataAvailable'. –