2009-07-06 10 views
1

J'ai un code réseau pour traiter une connexion TCP arbitraire..NET NetworkStream Lecture lenteur

Tout semble fonctionner comme prévu mais semble lent. Quand j'ai profilé le code, il semble passer 600 ms dans NetworkStream.Read() et je me demande comment l'améliorer. J'ai manipulé les tailles des tampons et alterné entre un tampon massif pour lire toutes les données en une fois ou un petit qui devrait concaténer les données dans un StringBuilder. Actuellement, le client que j'utilise est un navigateur Web, mais ce code est générique et il se peut qu'il ne s'agisse pas de données HTTP qui lui sont envoyées. Des idées?

Mon code est le suivant:

public void StartListening() 
    { 
     try 
     { 
      lock (oSyncRoot) 
      { 
       oTCPListener = new TcpListener(oIPaddress, nPort); 

       // fire up the server 
       oTCPListener.Start(); 

       // set listening bit 
       bIsListening = true; 
      } 

      // Enter the listening loop. 
      do 
      { 
       // Wait for connection 
       TcpClient newClient = oTCPListener.AcceptTcpClient(); 

       // queue a request to take care of the client 
       oThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient); 
      } 
      while (bIsListening); 
     } 
     catch (SocketException se) 
     { 
      Logger.Write(new TCPLogEntry("SocketException: " + se.ToString())); 
     } 
     finally 
     { 
      // shut it down 
      StopListening(); 
     } 
    } 

    private void ProcessConnection(object oClient) 
    { 

     TcpClient oTCPClient = (TcpClient)oClient; 
     try 
     { 
      byte[] abBuffer = new byte[1024]; 
      StringBuilder sbReceivedData = new StringBuilder(); 

      using (NetworkStream oNetworkStream = oTCPClient.GetStream()) 
      { 
       // set initial read timeout to nInitialTimeoutMS to allow for connection 
       oNetworkStream.ReadTimeout = nInitialTimeoutMS; 

       int nBytesRead = 0; 

       do 
       { 
        try 
        { 
         bool bDataAvailable = oNetworkStream.DataAvailable; 

         while (!bDataAvailable) 
         { 
          Thread.Sleep(5); 
          bDataAvailable = oNetworkStream.DataAvailable; 
         } 

         nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length); 

         if (nBytesRead > 0) 
         { 
          // Translate data bytes to an ASCII string and append 
          sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead)); 
          // decrease read timeout to nReadTimeoutMS second now that data is coming in 
          oNetworkStream.ReadTimeout = nReadTimeoutMS; 

         } 
        } 
        catch (IOException) 
        { 
         // read timed out, all data has been retrieved 
         nBytesRead = 0; 
        } 
       } 
       while (nBytesRead > 0); 

       //send the data to the callback and get the response back 
       byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToString(), oTCPClient); 
       if (abResponse != null) 
       { 
        oNetworkStream.Write(abResponse, 0, abResponse.Length); 
        oNetworkStream.Flush(); 
       } 
      } 
     } 
     catch (Exception e) 
     { 
      Logger.Write(new TCPLogEntry("Caught Exception " + e.StackTrace)); 
     } 
     finally 
     { 
      // stop talking to client 
      if (oTCPClient != null) 
      { 
       oTCPClient.Close(); 
      } 
     } 
    } 

Edit: je reçois à peu près les mêmes chiffres sur deux machines entièrement séparées (ma machine de développement XP et 2003 une boîte dans une colo). J'ai mis un peu de temps dans le code autour des parties concernées (en utilisant System.Diagnostic.StopWatch) et de vidage à un journal:

 
7/6/2009 3:44:50 PM : Debug : While DataAvailable took 0 ms 
7/6/2009 3:44:50 PM : Debug : Read took 531 ms 
7/6/2009 3:44:50 PM : Debug : ProcessConnection took 577 ms 
+0

J'ai le même problème. Je vois votre solution dans ce post, mais que diriez-vous de la nReadTimeOutMS, et bTurboMode. Pourriez-vous me donner une description complète?Je suis très intéressé et apprécié si vous partagez avec moi ce cours. Merci d'avance. – olidev

+1

Mon implémentation était très basique, j'en ai ré-écrit un gros morceau après ce post pour le faire correctement. Le point crucial est que vous ne comptez que sur les délais, etc., si le client expéditeur ne vous dit pas combien de données sont envoyées. Ma mise en œuvre récente a vérifié les en-têtes pour voir combien de données étaient envoyées et quand il avait lu tout ce que j'avais arrêté de lire. –

Répondre

0

Après quelques recherches, il semble que la seule façon d'accélérer c'est de briser après avoir été lu première x octets. Le retard semble être sur la deuxième lecture. Si je change le tampon à 8096 octets (probablement le maximum ma demande sera envoyée à tout coup) et de briser ici:

 if (nBytesRead > 0) 
     { 
      // Translate data bytes to an ASCII string and append 
      sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead)); 

      if (bTurboMode) 
      { 
        break; 
      } 
      else 
      { 
        // decrease read timeout to nReadTimeoutMS second now that data is coming in 
        oNetworkStream.ReadTimeout = nReadTimeoutMS; 
      } 
     } 

Puis le temps de réponse va de 600ms à environ 80ms. C'est une solution acceptable pour moi actuellement. Je peux basculer le bTurboMode de l'application appelant et accélérer les choses sensiblement pour ce cas

+1

C'est une manière hacky de le faire et ne devrait pas être fait de cette façon. J'ai tout réécrit pour utiliser les variables d'en-tête HTTP afin que je sache combien de données j'attendais ... –

2

Je vous recommande d'utiliser Microsoft Network Monitor ou quelque chose comme ça pour voir ce qui se passe sur en termes de ces 600ms. NetworkStream est un logiciel de gestion de réseau. Lorsque vous observez son comportement, tenez toujours compte de ce que fait le réseau.

1

Un autre vote pour l'utilisation d'un logiciel de surveillance de réseau. Moniteur réseau ou WireShark devrait faire. Assurez-vous d'enregistrer l'heure à laquelle l'appel networkstream.read commence et se termine dans votre programme afin que vous puissiez savoir où se sont produits les événements du programme dans le trafic réseau enregistré.

Je vous recommande également d'attendre que la propriété NetworkStream.DataAvailable devienne true avant d'appeler la méthode Read et d'enregistrer l'heure à laquelle elle devient également vraie. Si votre moniteur réseau affiche des données arrivant 600 ms avant que votre programme indique qu'il peut être lu, quelque chose d'autre sur votre ordinateur peut bloquer le paquet - par ex. antivirus ou votre pare-feu.

Addendum 06/07/2009 15h12 EDT:

Les informations de synchronisation supplémentaire que vous avez envoyé intéressant. Si les données sont disponibles, pourquoi cela prend-il autant de temps à lire? J'ai couru votre code sur ma machine de développement, et tous deux attendaient dataavailable et la fonction de lecture elle-même sortait comme 0 millisecondes. Êtes-vous sûr de disposer des derniers Service Packs, etc. installés? Je cours Visual Studio Professional 2005 avec .NET 2.0.50727. J'ai également installé .NET 3.0 et 3.5, mais je ne pense pas que VS 2005 les utilise. Avez-vous une nouvelle installation de système d'exploitation (machine réelle ou virtuelle) sans programmes supplémentaires (même/spécialement ceux "requis" par l'informatique de l'entreprise) que vous pourriez essayer?

Voici le code que je courais:

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Net; 
using System.Net.Sockets; 
using System.IO; 
using System.Threading; 
using System.Diagnostics; 

namespace stackoverflowtest 
{ 
    class Program 
    { 

     static private object oSyncRoot = new object(); 

     static private TcpListener oTCPListener; 

     static private IPAddress oIPaddress = IPAddress.Parse("10.1.1.109"); 

     static private int nPort = 8009; 

     static bool bIsListening = true; 





     static void Main(string[] args) 
     { 
      StartListening(); 
      Thread.Sleep(500000); 
      bIsListening = false; 
     } 

     public static void StartListening() 
     { 
      try 
      { 
       lock (oSyncRoot) 
       { 
        oTCPListener = new TcpListener(oIPaddress, nPort); 

        // fire up the server 
        oTCPListener.Start(); 

        // set listening bit 
        bIsListening = true; 
       } 

       // Enter the listening loop. 
       do 
       { 
        // Wait for connection 
        TcpClient newClient = oTCPListener.AcceptTcpClient(); 



        // queue a request to take care of the client 
        ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessConnection), newClient); 
       } 
       while (bIsListening); 
      } 
      catch (SocketException se) 
      { 
       Console.WriteLine("SocketException: " + se.ToString()); 
      } 
      finally 
      { 
       // shut it down 
       //StopListening(); 
      } 
     } 

     private static void ProcessConnection(object oClient) 
     { 

      TcpClient oTCPClient = (TcpClient)oClient; 
      try 
      { 
       byte[] abBuffer = new byte[1024]; 
       StringBuilder sbReceivedData = new StringBuilder(); 

       using (NetworkStream oNetworkStream = oTCPClient.GetStream()) 
       { 
        int nInitialTimeoutMS = 1000; 
        // set initial read timeout to nInitialTimeoutMS to allow for connection 
        oNetworkStream.ReadTimeout = nInitialTimeoutMS; 

        int nBytesRead = 0; 

        do 
        { 
         try 
         { 
          bool bDataAvailable = oNetworkStream.DataAvailable; 
          Stopwatch sw = new Stopwatch(); 
          while (!bDataAvailable) 
          { 
           Thread.Sleep(5); 
           bDataAvailable = oNetworkStream.DataAvailable; 
          } 
          Console.WriteLine("DataAvailable loop took " + sw.ElapsedMilliseconds); 

          sw.Reset(); 
          nBytesRead = oNetworkStream.Read(abBuffer, 0, abBuffer.Length); 
          Console.WriteLine("Reading " + nBytesRead + " took " + sw.ElapsedMilliseconds); 
          if (nBytesRead > 0) 
          { 
           // Translate data bytes to an ASCII string and append 
           sbReceivedData.Append(Encoding.UTF8.GetString(abBuffer, 0, nBytesRead)); 
           // decrease read timeout to nReadTimeoutMS second now that data is coming in 
           int nReadTimeoutMS = 100; 
           oNetworkStream.ReadTimeout = nReadTimeoutMS; 

          } 
         } 
         catch (IOException) 
         { 
          // read timed out, all data has been retrieved 
          nBytesRead = 0; 
         } 
        } 
        while (nBytesRead > 0); 

        byte[] abResponse = new byte[1024]; 
        for (int i = 0; i < abResponse.Length; i++) 
        { 
         abResponse[i] = (byte)i; 
        } 
        oNetworkStream.Write(abResponse, 0, abResponse.Length); 
        oNetworkStream.Flush(); 

        //send the data to the callback and get the response back 
        //byte[] abResponse = oClientHandlerDelegate(sbReceivedData.ToString(), oTCPClient); 
        //if (abResponse != null) 
        //{ 
        // oNetworkStream.Write(abResponse, 0, abResponse.Length); 
        // oNetworkStream.Flush(); 
        //} 
       } 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine("Caught Exception " + e.StackTrace); 
      } 
      finally 
      { 
       // stop talking to client 
       if (oTCPClient != null) 
       { 
        oTCPClient.Close(); 
       } 
      } 
     } 

    } 
} 
+0

J'ai essayé de tourner sur une machine complètement propre et j'ai toujours le même décalage. L'exécution de votre code, cependant, ne traîne pas, donc ça doit être comme je l'appelle. Incidemment; votre code ne démarre pas le chronomètre à nouveau après la réinitialisation quand je pense qu'il devrait? –

+0

Vous utilisez une machine différente, mais le même réseau ... Jetez un coup d'oeil avec le Moniteur réseau! –

+0

L'exécution du code de sskuce sur la même machine ne traîne pas si sûrement que ça ne peut pas être le réseau? –