2009-07-06 14 views
1

Ich habe einige Netzwerk-Code, um eine beliebige TCP-Verbindung zu verarbeiten..NET NetworkStream Lesen Langsamkeit

Es scheint alles wie erwartet zu funktionieren, scheint aber langsam. Wenn ich den Code profiliert habe, scheint er gut 600 ms in NetworkStream.Read() zu verbringen und ich frage mich, wie ich ihn verbessern kann. Ich habe mit den Puffergrößen herumgespielt und zwischen einem massiven Puffer gewechselt, um alle Daten in einem Durchgang oder einem kleinen zu lesen, der die Daten in einen StringBuilder verketten sollte. Momentan ist der Client, den ich benutze, ein Webbrowser, aber dieser Code ist generisch und es kann sein, dass es sich nicht um HTTP-Daten handelt, die an ihn gesendet werden. Irgendwelche Ideen?

Mein Code ist dies:

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: Ich ungefähr die gleichen Zahlen auf zwei völlig getrennten Maschinen bekommen (meine XP Entwicklungsmaschine und einen Kasten 2003 in einem Colo). Ich habe einige Timing in den Code um die relevanten Teile setzen (mit System.Diagnostic.StopWatch) und kippen ihn in ein Protokoll:

 
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

Ich habe das gleiche Problem. Ich sehe Ihre Lösung in diesem Beitrag, aber wie wäre es mit den nReadTimeOutMS und bTurboMode. Können Sie mir eine vollständige Beschreibung geben?Ich bin sehr interessiert und geschätzt, wenn Sie diese Klasse mit mir teilen. Danke im Voraus. – olidev

+1

Meine Implementierung war sehr einfach, ich schrieb einen großen Teil davon nach diesem Beitrag um es richtig zu machen. Der Kern der Sache ist, dass Sie sich nur auf Timeouts usw. verlassen, wenn der sendende Client Ihnen nicht sagt, wie viele Daten gesendet werden. Meine neuere Implementierung hat die Header überprüft, um zu sehen, wie viele Daten gesendet wurden und wann alles gelesen wurde, das ich nicht mehr gelesen habe. –

Antwort

0

Nach einigen mehr Forschung scheint es, dass der einzige Weg, dies zu beschleunigen, ist zu lesen zu brechen nach haben die ersten x Bytes ist. Die Verzögerung scheint bei der zweiten Lesung zu sein. Wenn ich den Puffer ändern 8096 Bytes sein (wahrscheinlich der max meine Anwendung jeden einmal gesendet) und bricht hier:

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

Dann wird die Reaktionszeit von 600 ms bis etwa 80 ms geht. Dies ist derzeit eine akzeptable Lösung für mich. Ich kann den bTurboMode von der aufrufenden Anwendung umschalten und die Dinge für diesen Fall wesentlich beschleunigen.

+1

Dies ist ein hacky Weg und sollte nicht so gemacht werden. Ich habe alles neu geschrieben, um die HTTP-Header-Variablen zu verwenden, so dass ich wusste, wie viele Daten ich erwartete ... –

2

Ich empfehle Sie Microsoft Network Monitor oder etwas ähnliches, um zu sehen, was los ist in Bezug auf diese 600ms. NetworkStream ist ein Stück Netzwerk-Software - wenn man sich das Verhalten anschaut, muss man immer überlegen, was das Netzwerk macht.

1

Eine weitere Abstimmung für die Verwendung von Netzwerküberwachungssoftware. Entweder Network Monitor oder WireShark sollte tun. Stellen Sie sicher, dass Sie aufzeichnen, zu welcher Zeit der Aufruf von networkstream.read in Ihrem Programm beginnt und endet, damit Sie wissen können, wo im aufgezeichneten Netzwerkverkehr Ihre Programmereignisse aufgetreten sind.

Außerdem würde ich empfehlen, auf die Eigenschaft NetworkStream.DataAvailable zu warten, bevor sie die Read-Methode aufruft, und die Zeit aufzuzeichnen, die sie ebenfalls wahr wird. Wenn Ihr Netzwerkmonitor Daten anzeigt, die 600 ms ankommen, bevor Ihr Programm anzeigt, dass sie gelesen werden können, kann etwas anderes auf Ihrem Computer das Paket halten - z. Antivirus oder Ihre Firewall.

Nachtrag 2009.07.06 03.12 EDT:

Die zusätzliche Zeitinformation geschrieben Sie ist interessant. Wenn Daten verfügbar sind, warum dauert das Lesen so lange? Ich habe Ihren Code auf meinem Entwicklungscomputer ausgeführt, und beide warten auf verfügbare Daten und die Lesefunktion selbst erscheint als 0 Millisekunden. Sind Sie sicher, dass Sie die neuesten Service Packs usw. installiert haben? Ich führe Visual Studio Professional 2005 mit .NET 2.0.50727 aus. Ich habe auch .NET 3.0 und 3.5 installiert, aber ich glaube nicht, dass VS 2005 diese verwendet. Haben Sie eine neue Betriebssysteminstallation (echte oder virtuelle Maschine) ohne zusätzliche Programme (sogar solche, die von der IT des Unternehmens "benötigt" werden), die Sie ausprobieren könnten?

Hier ist der Code, den ich lief:

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

Ich habe versucht, auf einer völlig sauberen Maschine zu laufen und immer noch die gleiche Verzögerung zu bekommen. Das Ausführen des Codes verzögert sich jedoch nicht, daher muss es so sein, wie ich es nenne. Übrigens; Dein Code startet die Stoppuhr nach dem Reset nicht wieder wenn ich denke das sollte? –

+0

Sie arbeiten auf einem anderen Computer, aber im selben Netzwerk ... Sehen Sie sich Network Monitor an! –

+0

Laufen sskuce Code auf der gleichen Maschine nicht Lag so sicher kann es nicht das Netzwerk sein? –

Verwandte Themen