1

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

这一切似乎都按预期工作,但似乎很慢。当我分析代码时,它似乎在 NetworkStream.Read() 中花费了 600 毫秒,我想知道如何改进它。我已经摆弄了缓冲区大小,并在一个巨大的缓冲区之间交替读取所有数据,或者一个小的缓冲区应该将数据连接到一个 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 开发机器和 colo 中的 2003 盒子)上得到大致相同的数字。我已经在相关部分的代码中添加了一些时间(使用 System.Diagnostic.StopWatch)并将其转储到日志中:

2009 年 7 月 6 日下午 3:44:50:调试:虽然 DataAvailable 花了 0 毫秒
2009 年 7 月 6 日下午 3:44:50:调试:读取耗时 531 毫秒
2009 年 7 月 6 日下午 3:44:50:调试:ProcessConnection 花了 577 毫秒
4

3 回答 3

2

我建议您使用 Microsoft Network Monitor 或类似的工具来查看这 600 毫秒的情况。NetworkStream 是一款网络软件 - 在查看其行为时,请始终考虑网络在做什么。

于 2009-07-06T13:16:39.840 回答
1

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

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

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

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

    }
}
于 2009-07-06T13:32:28.267 回答
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;
            }
        }

然后响应时间从 600 毫秒变为大约 80 毫秒。这是我目前可以接受的解决方案。我可以从调用应用程序中切换 bTurboMode 并在这种情况下大大加快速度

于 2009-07-07T13:01:24.640 回答