1

任意の 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();
            }
        }
    }

編集: 2 台のまったく別のマシン (私の XP 開発マシンと 2003 ボックスのコロコロ) でほぼ同じ数値が得られます。関連する部分のコードにタイミングを入れ (System.Diagnostic.StopWatch を使用)、ログにダンプしました。

2009 年 7 月 6 日 3:44:50 PM: デバッグ: DataAvailable が 0 ミリ秒かかった間
2009/7/6 3:44:50 PM: デバッグ: 読み取りに 531 ミリ秒かかりました
2009 年 7 月 6 日 3:44:50 PM: デバッグ: ProcessConnection に 577 ミリ秒かかりました
4

3 に答える 3

2

Microsoft Network Monitor などを使用して、これらの 600 ミリ秒で何が起こっているかを確認することをお勧めします。NetworkStream はネットワーク ソフトウェアの一部です。その動作を確認するときは、ネットワークが何を行っているかを常に考慮してください。

于 2009-07-06T13:16:39.840 に答える
1

ネットワーク監視ソフトウェアの使用に対する別の投票。NetworkMonitorまたはWireSharkのいずれかで実行できます。networkstream.read呼び出しがプログラムで開始および終了する時刻を記録して、記録されたネットワークトラフィックのどこでプログラムイベントが発生したかを確認できるようにしてください。

また、Readメソッドを呼び出す前に、NetworkStream.DataAvailableプロパティがtrueになるのを待って、trueになる時間を記録することをお勧めします。プログラムが読み取り可能であることを示す600ミリ秒前にネットワークモニターに到着したデータが表示される場合は、コンピューター上の他の何かがパケットを保持している可能性があります(ウイルス対策やファイアウォールなど)。

補遺2009/7/615:12EDT:

あなたが投稿した追加のタイミング情報は興味深いものです。データが利用できる場合、なぜ読み取るのにそれほど時間がかかるのですか?開発マシンでコードを実行しましたが、データが利用可能になるのを待っているだけでなく、読み取り関数自体も0ミリ秒として出力されます。最新のサービスパックなどがインストールされていますか?Visual StudioProfessional2005を.NET2.0.50727で実行しています。.NET 3.0と3.5もインストールしていますが、VS2005ではそれらを使用していないと思います。これを試すことができる追加のプログラム(特に企業のITが「必要とする」プログラム)がない、新しいOSインストール(実マシンまたは仮想マシン)がありますか?

これが私が実行したコードです:

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バイトが読み取られた後に中断することであるようです。遅延は2回目の読み取りにあるようです。バッファを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を切り替えて、この場合は大幅に高速化できます

于 2009-07-07T13:01:24.640 に答える