简体   繁体   English

应用程序运行时C#TCP套接字性能下降

[英]C# TCP socket performance degrading as application runs

I wrote a C# server application. 我写了一个C#服务器应用程序。 The server utilizes Asynchronous TCP sockets. 服务器使用异步TCP套接字。

The packets are 80-180 bytes of data. 数据包是80-180字节的数据。

For performance testing I have a single client connect and send packets continuously. 对于性能测试,我有一个客户端连接并连续发送数据包。 With debugging on the first 100 packets (0-100) receive in roughly 5 seconds. 通过调试前100个数据包(0-100)在大约5秒内接收。 By the time the server received packets #300-400 it takes roughly 30 seconds to receive the packets. 当服务器收到数据包#300-400时,接收数据包大约需要30秒。 The performance continues to degrade as more receives occur. 随着更多接收发生,性能继续下降。

I looked around and have not been able to find a solution. 我环顾四周,一直无法找到解决方案。 I have tried setting the Socket.NoDelay flag in case the Nagle algorithm was inhibiting the server. 我已经尝试设置Socket.NoDelay标志,以防Nagle算法禁止服务器。

I have disabled all functions within the server; 我已经禁用了服务器中的所有功能; so that it is only receiving to ensure I wasn't losing performance in other code. 所以它只是收到以确保我没有失去其他代码中的性能。

I have also checked my CPU utilization and it is ~13%. 我还检查了我的CPU利用率,它是~13%。 I have over 2 GB of free memory. 我有超过2 GB的可用内存。 When running the application the ram is NOT constantly growing and utilization is minimal. 在运行应用程序时,ram不会不断增长,利用率也很低。

I am at a loss as to what to debug and look into next... 关于调试什么,看看下一个我感到很遗憾......

EDIT: Added Code Sample 编辑:添加代码示例

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

Edit: AddLogText() function added. 编辑:添加AddLogText()函数。 This function is used to add text to a Text Box that is in the UI. 此函数用于将文本添加到UI中的文本框。

//Delegate - enables asychronous calls for setting the text property of the tb_ListeningLog // Delegate - 启用异步调用以设置tb_ListeningLog的text属性

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

I'm taking a bit of a shot in the dark with this answer, but the code you've posted certainly helps. 我用这个答案在黑暗中拍了一下,但你发布的代码肯定有帮助。

The reason you're probably seeing slow performance as time goes on is because of the code in your readDataCallback method. 随着时间的推移你可能会看到性能下降的原因是因为readDataCallback方法中的代码。 The way you have it set up, the processing of the data is done before you go for another receive. 您设置的方式,数据处理在您再次接收之前完成。 This means that as the length of the processing increases, the duration between receiving your data increases. 这意味着随着处理长度的增加,接收数据之间的持续时间也会增加。

I don't know what code is in a lot of your methods, but you should generally look at any loops that may be taking a while to finish. 我不知道你的很多方法中有什么代码,但是你应该看看可能需要一段时间才能完成的任何循环。 If you're having trouble finding the bottleneck by looking through your code, try finding which methods take the longest to finish and continue to narrow your code down. 如果您在查找代码时遇到问题,请尝试查找哪些方法花费的时间最长,然后继续缩小代码范围。

For instance (I'm guessing that the bottleneck is in this area of code): 例如(我猜测瓶颈在这个代码区域):

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");
    }
}

Just observe the amount of time between each method with your eyes, or make it easier and use a Stopwatch or DateTime to show exact time. 只需观察每种方法与眼睛之间的时间量,或使其更容易,并使用StopwatchDateTime时间来显示确切的时间。

Also, if you find that the behavior of the code cannot be made more efficient, you could toy around with the idea of processing the data in a separate thread. 此外,如果您发现代码的行为无法提高效率,您可以想到在单独的线程中处理数据。 I'm assuming that this behavior isn't desired, though, because of the question at hand. 我假设不希望这种行为,因为手头的问题。


For your AddLogText method, try using tb_ListeningLog.Text.AppendText instead of +=. 对于AddLogText方法,请尝试使用tb_ListeningLog.Text.AppendText而不是+ =。

I am not sure why you have such a long piece of code to read more data. 我不知道为什么你有这么长的代码来读取更多的数据。 Also, try placing the message in a Queue which can be processed by a different thread. 另外,尝试将消息放入可由不同线程处理的队列中。

Here is an implementation I use: 这是我使用的实现:

// 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);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM