2009-07-06 16 views
1

任意のTCP接続を処理するためのネットワークコードがあります。.NET NetworkStream低速読み取り

これはすべて期待どおりに機能するようですが、遅いようです。コードをプロファイリングすると、NetworkStream.Read()で600ミリ秒を費やすように見え、それを改善する方法が不思議です。私は、バッファサイズを手抜きして、大量のバッファを交互に読み込んで、すべてのデータを読み込んだり、小さなデータをStringBuilderに連結したりする必要がありました。現在私が使用しているクライアントはWebブラウザですが、このコードは一般的なもので、送信される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(); 
      } 
     } 
    } 

編集:

私のコードはこれである私は、大きく分けて2台の完全に別々のマシン(私の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

いくつかのより多くの研究の後、これをスピードアップするための唯一の方法は、xバイト最初に読み込まれた後にブレークするようです:

ここで私が走ったコードです。遅延は2回目の読み込みのようです。私は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; 
      } 
     } 

を次に応答時間は600msの約80ミリ秒に行きます。これは私にとって現在受け入れられる解決策です。私はbTurboModeを呼び出し元のアプリケーションから切り離し、この事件のために事態を大幅にスピードアップすることができます

+1

これはやっかいな方法であり、このようにしてはいけません。私はHTTPヘッダー変数を使用するためにすべてを書き直して、どれだけのデータが予想されているかを知っていました。 –

2

私はあなたが何が起こっているのか確認するためにMicrosoftネットワークモニタまたはそれのようなものを使用することをお勧めそれらの600msの点で。 NetworkStreamはネットワーキングソフトウェアです。その動作を見て、常にネットワークが何をしているのかを考えます。

1

ネットワーク監視ソフトウェアを使用する別の投票。ネットワークモニタかWireSharkのどちらかがすべきです。プログラムのイベントが記録されたネットワークトラフィックのどこにあるかを知ることができるように、networkstream.read呼び出しの開始時刻と終了時刻を記録してください。

また、Readメソッドを呼び出す前にNetworkStream.DataAvailableプロパティがtrueになるのを待って、それがtrueになる時刻を記録することをお勧めします。ネットワークモニタに、プログラムから読み込めることを示す600 ms前に到着したデータが表示されている場合、コンピュータの他の何かがパケットを保持している可能性があります。ウイルス対策、またはファイアウォール。

補遺2009年7月6日15:12 EDT:

あなたが投稿余分なタイミング情報が面白いです。データが利用可能な場合は、なぜ読みに時間がかかりますか?私は自分の開発マシンでコードを実行しました、そして、データ利用可能なのを待っていて、読み取り機能自体は0ミリ秒として出てきます。最新のサービスパックなどをインストールしてもよろしいですか?私は.NET 2.0.50727でVisual Studio Professional 2005を実行しています。私も.NET 3.0と3.5がインストールされているが、私はVS 2005がそれらを使用しているとは思わない。あなたはこれを試すことができる余分なプログラム(企業ITによって「必要」さえあるもの)を持たない新鮮なOSインストール(実マシンまたは仮想マシン)をお持ちですか?

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のコードを実行しても遅くなることはないので、ネットワークにすることはできません。 –

関連する問題