2009-07-06 90 views
1

我有一些網絡代碼來處理任意TCP連接。.NET NetworkStream讀取緩慢

這一切似乎按預期工作,但似乎緩慢。當我對代碼進行剖析時,似乎在NetworkStream.Read()中花費了600毫秒,我想知道如何改進它。我已經調整了緩衝區大小,並在一個龐大的緩衝區之間交替讀取,以便一次讀取所有數據,或者將數據連接成一個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(); 
      } 
     } 
    } 

編輯:我得到大致在兩個完全獨立的機器(我的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 2011-06-08 12:49:44

+1

我的實現是非常基本的,我在這篇文章之後重新編寫了一大部分,以便正確執行。問題的關鍵在於,如果發送客戶端不告訴你有多少數據正在發送,你只能依靠超時等。我最近的實現檢查了標題,看看有多少數據正在發送,以及它何時讀取了我停止閱讀的所有內容。 – 2011-06-09 08:47:35

回答

0

一些調查研究後,似乎加快這唯一的辦法就是第一個x字節被讀取後打破。延遲似乎是在第二次閱讀。如果我改變緩衝區爲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毫秒去80ms左右。目前這是我可以接受的解決方案。我可以從調用應用程序中切換bTurboMode,並在這種情況下大幅提高速度

2

我建議你使用Microsoft網絡監視器或類似的東西,看看這是怎麼回事就那600毫秒而言。 NetworkStream是一款網絡軟件 - 在考察其行爲時,始終考慮網絡的運行情況。

1

另一個使用網絡監控軟件的投票。 Network Monitor或WireShark都應該這樣做。確保記錄networkstream.read調用在程序中開始和結束的時間,以便您可以知道程序事件發生時錄製網絡流量的位置。

此外,我建議在調用Read方法之前等待NetworkStream.DataAvailable屬性爲true,並記錄它變爲true的時間。如果您的網絡監視器顯示的數據在您的程序指示可以讀取之前600毫秒到達,您計算機上的其他內容可能會阻止數據包 - 例如,防病毒軟件或防火牆。

附錄2009年7月6日3點12分美國東部時間:

您發佈的額外時間信息是有趣的。如果數據可用,爲什麼閱讀這麼長時間?我在開發機器上運行你的代碼,並且都等待dataavailable,讀取函數本身出來的時間爲0毫秒。你確定你有最新的服務包等安裝?我使用.NET 2.0.50727運行Visual Studio Professional 2005。我也安裝了.NET 3.0和3.5,但我不認爲VS 2005正在使用這些。你是否有新的操作系統安裝(真實或虛擬機),沒有額外的程序(甚至是企業IT「需要」的程序),你可以試試這個程序?

這是我跑的代碼:

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

我試過在一臺完全乾淨的機器上運行,仍然得到相同的滯後。然而,運行你的代碼不會滯後,所以它一定是我稱之爲的方式。 順便說一下;當我認爲應該重置後,你的代碼不會重新啓動秒錶? – 2009-07-07 09:41:19