代码之家  ›  专栏  ›  技术社区  ›  Gordon Thompson

.NET NetworkStream读取速度缓慢

  •  1
  • Gordon Thompson  · 技术社区  · 15 年前

    我有一些网络代码来处理任意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开发机器和colo中的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
    
    3 回复  |  直到 15 年前
        1
  •  2
  •   John Saunders    15 年前

    我建议您使用MicrosoftNetworkMonitor或类似的工具来查看这些600ms的运行情况。NETWorkScript是一个网络软件——当查看它的行为时,总是考虑网络正在做什么。

        2
  •  1
  •   Sam Skuce    15 年前

    再次投票支持使用网络监控软件。网络监视器或WireShark都可以。确保记录networkstream.read调用在程序中开始和结束的时间,以便知道程序事件发生在记录的网络流量中的何处。

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

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

    你发布的额外时间信息很有趣。如果数据可用,为什么要花这么长时间读取?我在我的开发机器上运行了你的代码,在等待dataavailable和read函数本身的结果都是0毫秒。您确定已安装最新的service Pack等吗?我正在用.NET2.0.50727运行VisualStudioProfessional2005。我也安装了.NET3.0和3.5,但我认为VS2005没有使用这些。您是否有一个新的操作系统安装(真实或虚拟机),没有额外的程序(甚至/特别是公司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();
                    }
                }
            }
    
        }
    }
    
        3
  •  0
  •   Gordon Thompson    15 年前

    经过更多的研究,似乎唯一的加速方法是在读取第一个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毫秒到大约80毫秒。这是我目前可以接受的解决方案。我可以从调用应用程序切换bTurboMode,并在这种情况下大大加快速度