5

C# サーバー アプリケーションを作成しました。サーバーは非同期 TCP ソケットを使用します。

パケットは 80 ~ 180 バイトのデータです。

パフォーマンス テストでは、単一のクライアントを接続してパケットを継続的に送信します。最初の 100 パケット (0-100) のデバッグを使用すると、約 5 秒で受信します。サーバーがパケット #300-400 を受信するまでに、パケットを受信するのに約 30 秒かかります。より多くの受信が発生するにつれて、パフォーマンスは低下し続けます。

私は周りを見回しましたが、解決策を見つけることができませんでした。Nagle アルゴリズムがサーバーを抑制している場合に備えて、Socket.NoDelay フラグを設定してみました。

サーバー内のすべての機能を無効にしました。他のコードでパフォーマンスが低下していないことを確認するためだけに受信しているようにします。

CPU 使用率も確認しましたが、約 13% です。2 GB 以上の空きメモリがあります。アプリケーションを実行しているとき、RAM は常に増加するわけではなく、使用率は最小限です。

何をデバッグして次に調べるべきか途方に暮れています...

編集:コードサンプルを追加

public void StartListening()
    {

        try
        {
            IPAddress ipAddress = IPAddress.Parse("192.168.2.60");
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, m_Port);
            m_MainSocket = new Socket(localEndPoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
            m_MainSocket.NoDelay = true;
            m_MainSocket.Bind(localEndPoint);
            m_MainSocket.Listen(10);
            m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);

            System.Diagnostics.Debug.WriteLine("Listening on:Local IP Address: " + localEndPoint.Address.ToString() + " Port :" + localEndPoint.Port.ToString() + "\n");
        }
        catch (SocketException se)
        {
            System.Diagnostics.Debug.WriteLine("Listening Exception \n");
            System.Diagnostics.Debug.WriteLine(se.Message);
        }
    }

    void clientConnected(IAsyncResult ar)
    {
        try
        {
            SocketState state = new SocketState(m_MainSocket.EndAccept(ar));
            Client client = new Client(state);

            if (client.SocketState.clientSocket.Connected)
            {
                System.Diagnostics.Debug.WriteLine("Client #?????? Connected \n");
                AddLogText("Client #?????? Connected \r\n\r\n");
                waitForData(client);
                SetSendButton(true);
            }

            m_MainSocket.BeginAccept(new AsyncCallback(clientConnected), null);
        }
        catch (ObjectDisposedException)
        {
           System.Diagnostics.Debug.WriteLine("Client Connected: Socket has been closed\n");
        }
        catch (SocketException se)
        {
            System.Diagnostics.Debug.WriteLine("Client Connected Exception \n");
            System.Diagnostics.Debug.WriteLine(se.Message);
        }
    }

    void waitForData(Client client)
    {
        try
        {
            SocketState state = new SocketState(client.SocketState.clientSocket);
            client.SocketState.clientSocket = null; 
            client.SocketState = state;
            client.SocketState.clientSocket.BeginReceive(client.SocketState.DataBuffer, 0, client.SocketState.DataBuffer.Length, SocketFlags.None, new AsyncCallback(readDataCallback), client);
        }
        catch (SocketException se)
        {
            System.Diagnostics.Debug.WriteLine("Wait For Data Exception \n");
            System.Diagnostics.Debug.WriteLine(se.Message);
        }

    }

    public void readDataCallback(IAsyncResult ar)
    {
        Client client = (Client)ar.AsyncState;
        try
        {                
            // Read data from the client socket.
            int iRx = client.SocketState.clientSocket.EndReceive(ar);
            client.SocketState.SB.Append(Encoding.ASCII.GetString(client.SocketState.DataBuffer, 0, iRx));
            string sPacketString = client.SocketState.SB.ToString();

            Server formServer = this;
            Packet_Helper packet_helper = new Packet_Helper(sPacketString, formServer);

            Packet packet = new Packet(sPacketString);
            client.SerialNumber = packet.SerialNumber;
            client.FirmwareVersion = packet.FirmwareVersion;
            client.ProductID = packet.ProductID;
            client.HardwareVersion = packet.HardwareVersion;
            if (!m_Clients.ContainsKey(packet.SerialNumber))
            {
                m_Clients.Add(packet.SerialNumber, client);
                UpdateClientList();
                string[] packets = client.refreshAll();
                for (int i = 0; i < packets.Length; i++)
                {
                    byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
                    client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
                    AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
                }
            }

            System.Diagnostics.Debug.WriteLine("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + "\n" + sPacketString + "\n");
            AddLogText("Read " + sPacketString.Length.ToString() + " bytes from " + client.SerialNumber + " \r\n");
            AddLogText(sPacketString.ToString() + "\r\n\r\n");

            waitForData(client);
        }
        catch (ObjectDisposedException)
        {
            System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
        }
        catch (SocketException se)
        {
            if (se.ErrorCode == 10054) // Error code for Connection reset by peer
            {
                string sclientSerial = "??????";
                if (client.SerialNumber != null || client.SerialNumber != "")
                    sclientSerial = client.SerialNumber;
                AddLogText("Client " + sclientSerial + " Disconnected" + "\r\n\r\n");
                System.Diagnostics.Debug.WriteLine("Client " + sclientSerial + " Disconnected" + "\n");

                m_Clients.Remove(sclientSerial);
                UpdateClientList();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("Read Data Exception \n");
                System.Diagnostics.Debug.WriteLine(se.Message);
            }
        }
    }


class SocketState
{
    private Socket m_ClientSocket;                   //Socket connection to the client
    private byte[] m_DataBuffer = new byte[256];        //Buffer to store the data sent by the client
    private StringBuilder m_SB = new StringBuilder();  //for building recieved data into a string 

    /// <summary>
    /// Gets or Sets the client Socket
    /// </summary>
    public Socket clientSocket
    {
        get { return m_ClientSocket; }
        set { m_ClientSocket = value; }
    }

    /// <summary>
    /// Gets the DataBuffer
    /// </summary>
    public byte[] DataBuffer
    {
        get { return m_DataBuffer; }
        set { DataBuffer = value; }
    }

    /// <summary>
    /// Gets or Sets the SB
    /// </summary>
    public StringBuilder SB
    {
        get { return m_SB; }
        set { m_SB = value; }
    }

    public SocketState(Socket socket)
    {
        m_ClientSocket = socket;
        m_ClientSocket.ReceiveBufferSize = 256;
        m_ClientSocket.NoDelay = true;
        //m_DataBuffer = Enumerable.Repeat((byte)0, 256).ToArray();
    }
}      

編集: AddLogText() 関数が追加されました。この関数は、UI にあるテキスト ボックスにテキストを追加するために使用されます。

//デリゲート - tb_ListeningLog のテキスト プロパティを設定するための非同期呼び出しを有効にします

delegate void AddLogTextCallback(string text);

private void AddLogText(string text)
    {
        // InvokeRequired required compares the thread ID of the
        // calling thread to the thread ID of the creating thread.
        // If these threads are different, it returns true.
        if (this.tb_ListeningLog.InvokeRequired)
        {
            AddLogTextCallback d = new AddLogTextCallback(AddLogText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.tb_ListeningLog.Text += text;
            tb_ListeningLog.SelectionStart = tb_ListeningLog.Text.Length;
            tb_ListeningLog.ScrollToCaret();
        }
    }
4

2 に答える 2

5

私はこの答えで暗闇の中で少しショットを撮っていますが、あなたが投稿したコードは確かに役に立ちます.

時間の経過とともにパフォーマンスが低下している可能性が高い理由は、readDataCallbackメソッド内のコードが原因です。このように設定すると、別の受信に進む前にデータの処理が完了します。これは、処理の長さが長くなるにつれて、データを受信する間隔が長くなることを意味します。

多くのメソッドにどのようなコードが含まれているかはわかりませんが、一般的に、完了するまでに時間がかかる可能性のあるループを確認する必要があります。コードを調べてもボトルネックを見つけるのが難しい場合は、終了までに最も時間がかかるメソッドを見つけて、引き続きコードを絞り込んでください。

たとえば(ボトルネックはコードのこの領域にあると推測しています):

if (!m_Clients.ContainsKey(packet.SerialNumber))
{
    m_Clients.Add(packet.SerialNumber, client);

    AddLogText("Running UpdateClientList\r\n");

    UpdateClientList();

    AddLogText("Doing client.refreshAll\r\n");

    string[] packets = client.refreshAll();

    AddLogText("Doing for loop\r\n");

    for (int i = 0; i < packets.Length; i++)
    {
        byte[] byteData = Encoding.ASCII.GetBytes(packets[i]);
        client.SocketState.clientSocket.BeginSend(byteData, 0, byteData.Length, SocketFlags.None, new AsyncCallback(SendCallback), client);
        AddPacketsSentText(packets[i] + "--" + (iSent++).ToString() + "\r\n\r\n");
    }
}

各メソッド間の時間を目で観察するか、簡単にStopwatchまたはを使用してDateTime正確な時間を表示します。

また、コードの動作をより効率的にすることができない場合は、別のスレッドでデータを処理するというアイデアを試すことができます。ただし、当面の質問のために、この動作は望ましくないと思います。


AddLogTextメソッドでtb_ListeningLog.Text.AppendTextは、+= の代わりに使用してみてください。

于 2012-04-27T15:46:43.097 に答える
0

より多くのデータを読み取るために、なぜこんなに長いコードがあるのか​​ わかりません。また、別のスレッドで処理できるキューにメッセージを配置してみてください。

これが私が使用する実装です:

// Read data from the client
private void ReadCallback(IAsyncResult ar)
{
    StateObject state = (StateObject)ar.AsyncState;
    Socket socket = state.workSocket;

    try
    {
        if (socket.Connected)
        {
            // Read the socket
            int bytesRead = socket.EndReceive(ar);

            // Deserialize objects
            foreach (MessageBase msg in MessageBase.Receive(socket, bytesRead, state))
            {
                // Add objects to the message queue
                lock (this.messageQueue)
                    messageQueue.Enqueue(msg);
            }

            // Notify any event handlers
            if (DataRecieved != null)
                DataRecieved(socket, bytesRead);

            // Asynchronously read more client data
            socket.BeginReceive(state.Buffer, state.readOffset, state.BufferSize - state.readOffset, 0,
                ReadCallback, state);
        }
        else
        {
            HandleClientDisconnect(socket);
        }
    }
    catch (SocketException)
    {
        HandleClientDisconnect(socket);
    }
}
于 2012-04-27T17:24:53.130 に答える