.NET NetworkStream Медлительность чтения

У меня есть сетевой код для обработки произвольного TCP-соединения.

Кажется, все работает, как ожидалось, но кажется медленным. Когда я профилировал код, он, похоже, провел 600 мс в NetworkStream.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();
            }
        }
    }

Редактировать: я получаю примерно одинаковые цифры на двух совершенно отдельных машинах (моя машина для разработки XP и коробка 2003 в коло). Я поместил немного времени в код вокруг соответствующих частей (используя System.Diagnostic.StopWatch) и выгрузил его в журнал:

06.07.2009 15:44:50: отладка: в то время как DataAvailable заняла 0 мс
06.07.2009 15:44:50: отладка: чтение заняло 531 мс
06.07.2009 15:44:50: отладка: ProcessConnection заняла 577 мс

3 ответа

Решение

После еще одного исследования кажется, что единственный способ ускорить это - сломаться после того, как будут прочитаны первые 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 из вызывающего приложения и существенно ускорить процесс в этом случае

Я рекомендую вам использовать Microsoft Network Monitor или что-то подобное, чтобы увидеть, что происходит с этими 600 мс. NETworkStream - это часть сетевого программного обеспечения - при рассмотрении его поведения всегда учитывайте, что делает сеть.

Еще один голос за использование программного обеспечения для мониторинга сети. Либо Сетевой монитор или WireShark должны делать. Убедитесь, что вы записываете, в какое время вызов networkstream.read начинается и заканчивается в вашей программе, чтобы вы могли знать, где в зарегистрированном сетевом трафике произошли события вашей программы.

Кроме того, я бы рекомендовал подождать, пока свойство NetworkStream.DataAvailable станет истинным, прежде чем вызывать метод Read, и записать время, когда оно также станет истинным. Если ваш сетевой монитор показывает данные, поступающие за 600 мс до того, как ваша программа показывает, что они могут быть прочитаны, возможно, что-то еще на вашем компьютере задерживает пакет - например, антивирус или брандмауэр.

Приложение 2009/7/6 15:12 EDT:

Дополнительная информация о времени, которую вы разместили, интересна. Если данные доступны, почему это занимает так много времени для чтения? Я запустил ваш код на своей машине для разработки, и ожидание доступных данных и сама функция чтения выдаются за 0 миллисекунд. Вы уверены, что у вас установлены последние пакеты обновлений и т. Д.? Я использую Visual Studio Professional 2005 с.NET 2.0.50727. У меня также установлены.NET 3.0 и 3.5, но я не думаю, что VS 2005 использует их. У вас есть свежая установка ОС (реальная или виртуальная машина) без каких-либо дополнительных программ (даже / особенно тех, которые "требуются" корпоративным ИТ), на которых вы могли бы это попробовать?

Вот код, который я запустил:

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 stackrutest
{
    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();
                }
            }
        }

    }
}
Другие вопросы по тегам