2009-07-06 5 views
1

임의의 TCP 연결을 처리 할 수있는 네트워크 코드가 있습니다..NET NetworkStream 느린 읽기

모두 예상대로 작동하지만 느리게 보입니다. 내가 코드를 프로파일 링했을 때 NetworkStream.Read()에서 600ms를 소비하는 것처럼 보였고이를 개선하는 방법이 궁금합니다. 나는 버퍼 크기를 집중적으로 다루었 다. 한 번에 모든 데이터를 읽거나 데이터를 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

+1

내 구현은 매우 기본적인 것이므로이 게시물 이후에 제대로 수행하기 위해 큰 덩어리를 다시 작성했습니다. 문제의 요지는 전송 클라이언트가 전송되는 데이터의 양을 알려주지 않으면 시간 초과 등에만 의존한다는 것입니다. 최근 구현에서는 헤더를 검사하여 전송되는 데이터의 양과 읽은 모든 것을 읽은 시간을 확인했습니다. –

답변

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

그런 다음 응답 시간에 대한 평균 80ms로 600ms의에서갑니다. 이것은 현재 나를위한 수용 가능한 해결책이다. 호출 응용 프로그램에서 bTurboMode를 토글하여이 경우 크게 속도를 높일 수 있습니다.

+1

이것은 해킹하는 방법이며 이렇게해서는 안됩니다. HTTP 헤더 변수를 사용하기 위해 모든 것을 다시 작성하여 얼마나 많은 데이터를 예상했는지 알 수있었습니다 ... –

2

내가 추천 당신이 무슨 일이 일어나고 있는지보고 Microsoft 네트워크 모니터 또는 같은 것을 사용 그 600ms의 관점에서. NetworkStream은 네트워크 소프트웨어입니다. 네트워크의 동작을 살펴볼 때 네트워크가하는 일을 항상 고려하십시오.

1

네트워크 모니터링 소프트웨어 사용에 대한 또 다른 투표. 네트워크 모니터 또는 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 부서에서 "필수"인 프로그램) 새 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 코드를 실행해도 래깅되지 않으므로 네트워크가 될 수 없습니다. –