.NET NetworkStream读取缓慢

我有一些网络代码来处理任意TCP连接。

这一切似乎按预期工作,但似乎很慢。 当我对代码进行分析时,似乎在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(); } } } 

编辑:我在两台完全独立的机器上得到大致相同的数字(我的XP开发机器和一个2003年的colo盒子)。 我已经在相关部分的代码中添加了一些时间(使用System.Diagnostic.StopWatch)并将其转储到日志中:

 7/6/2009 3:44:50 PM:调试:DataAvailable需要0 ms
 7/6/2009 3:44:50 PM:调试:读取耗时531 ms
 7/6/2009 3:44:50 PM:调试:ProcessConnection耗时577毫秒

我建议您使用Microsoft网络监视器或类似的东西来查看600毫秒的情况。 NetworkStream是一种网络软件 – 在查看其行为时,始终要考虑网络正在做什么。

另一个投票使用网络监控软件。 网络监视器或WireShark都应该这样做。 确保记录networkstream.read呼叫在您的程序中开始和结束的时间,以便您可以知道您的程序事件发生在记录的网络流量中的哪个位置。

另外,我建议在调用Read方法之前等待NetworkStream.DataAvailable属性变为true,并记录它变为true的时间。 如果您的网络监视器显示在程序指示可以读取之前600毫秒到达的数据,则计算机上的其他内容可能会阻止数据包 – 例如防病毒或防火墙。

附录2009/7/6美国东部时间下午3:12:

您发布的额外时间信息很有趣。 如果数据可用,为什么要花这么长时间阅读? 我在我的开发机器上运行你的代码,并且都在等待dataavailable和read函数本身都是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(); } } } } } 

经过一些更多的研究后,似乎加快这一点的唯一方法是在读取前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; } } 

然后响应时间从600ms到大约80ms。 这对我来说是一个可以接受的解决方案。 我可以从调用应用程序切换bTurboMode,并在这种情况下大大加快速度