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

.NET网络流读取缓慢

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

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

    一切似乎都按预期进行,但似乎进展缓慢。当我分析代码时,它似乎在NetworkStream中花费了600毫秒。Read(),我想知道如何改进它。我调整了缓冲区的大小,在一次性读取所有数据的大缓冲区和将数据连接成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();
                }
            }
        }
    

    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 回复  |  直到 16 年前
        1
  •  2
  •   John Saunders    16 年前

        2
  •  1
  •   Sam Skuce    16 年前

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

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

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

    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    16 年前

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