2009-07-06 5 views
1

У меня есть код сети для обработки произвольного TCP-соединения..NET NetworkStream Чтение медленности

Все, кажется, работает должным образом, но кажется медленным. Когда я профилировал код, он, кажется, потратил хорошие 600 мс в NetworkStream.Read(), и мне интересно, как его улучшить. Я искал размеры буфера и чередовался между массивным буфером для чтения всех данных за один раз или небольшой, который должен объединить данные в StringBuilder. В настоящее время клиент, которого я использую, является веб-браузером, но этот код является общим, и он может не быть HTTP-данными, которые отправляются на него. Есть идеи?

Мой код заключается в следующем:

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: я получаю примерно те же цифры на двух совершенно отдельных машинах (мою машину развития XP и 2003 в поле коло). Я положил некоторые сроки в код вокруг соответствующих частей (с использованием System.Diagnostic.StopWatch) и сбросить его в журнал:

 
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

У меня такая же проблема. Я вижу ваше решение в этом сообщении, но как насчет nReadTimeOutMS и bTurboMode. Не могли бы вы дать мне полное описание?Я очень заинтересован и ценю, если вы поделитесь со мной этим классом. Заранее спасибо. – olidev

+1

Моя реализация была очень простой, я переписал большой кусок ее после этого сообщения, чтобы сделать это правильно. Суть в том, что вы полагаетесь только на таймауты и т. Д., Если отправляющий клиент не сообщает вам, сколько данных отправляется. Моя недавняя реализация проверила заголовки, чтобы узнать, сколько данных было отправлено, и когда он прочитал все, что я прекратил читать. –

ответ

0

После еще некоторых исследований, кажется, что единственный способ ускорить этот процесс, чтобы сломать после первых й байт были прочитаны. Задержка, похоже, на втором чтении. Если изменить буфер быть 8096 байт (вероятно, не более мое заявление будет отправлено в любой один раз) и разбить здесь:

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

Тогда время отклика идет от 600 мс до 80 мс. Это приемлемое решение для меня в настоящее время. Я могу переключить bTurboMode из вызывающего приложения и ускорить процесс по существу для этого случая.

+1

Это хакерский способ сделать это, и этого не должно быть сделано. Я переписал все это, чтобы использовать переменные заголовка HTTP, чтобы я знал, сколько данных я ожидал ... –

2

Я рекомендую использовать Microsoft Network Monitor или нечто подобное, чтобы увидеть, что происходит с точки зрения этих 600 мс. NetworkStream - это часть сетевого программного обеспечения - когда вы смотрите на его поведение, всегда учитывайте, что делает сеть.

1

Еще одно голосование за использование программного обеспечения для мониторинга сети. Должен делать либо сетевой монитор, либо WireShark. Убедитесь, что вы записываете, в какое время начинается и заканчивается вызов networkstream.read, и вы можете узнать, где в записанном сетевом трафике произошли ваши программные события.

Кроме того, я бы рекомендовал, чтобы свойство NetworkStream.DataAvailable стало истинным, прежде чем вы вызовете метод Read и запишите время, когда оно станет истинным. Если ваш сетевой монитор показывает данные, достигающие 600 мс, прежде чем ваша программа укажет, что их можно прочитать, что-то еще на вашем компьютере может поддерживать пакет. антивирус или ваш брандмауэр.

Добавление 2009/7/6 3:12 вечера EDT:

Дополнительная информация времени вы вывесили интересно. Если доступны данные, почему он так долго читает? Я запустил ваш код на моей машине разработки, и оба ожидали, что данные будут доступны, а сама функция чтения выйдет как 0 миллисекунд. Уверены ли вы, что установлены последние пакеты обновлений и т. Д.? Я запускаю Visual Studio Professional 2005 с .NET 2.0.50727. У меня также установлены .NET 3.0 и 3.5, но я не думаю, что VS 2005 использует их. У вас есть новая установка ОС (реальная или виртуальная машина) без каких-либо дополнительных программ (даже в том числе «требуемых» корпоративными ИТ), на которые вы могли бы попробовать?

Вот код, я побежал:

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

Я пробовал работать на полностью чистой машине и все еще получаю тот же лаг. Однако запуск вашего кода не отстает, так что это должно быть так, как я его называю. Между прочим; ваш код не запускает секундомер снова после сброса, когда я думаю, что он должен? –

+0

Вы работаете на другой машине, но в той же сети ... Взгляните с помощью сетевого монитора! –

+0

Запуск кода sskuce на том же компьютере не отстает, так что это не может быть сеть? –

Смежные вопросы