System.net.Socket.beginReceive()中的高CPU使用率

时间:2017-05-02 19:18:46

标签: c# sockets asyncsocket

我有一个服务器软件,它有一个监听套接字,然后跨越多个套接字(10 -30),然后我将数据流传输到。

如果我启动我的应用程序,它在我的8个vCPU VM上使用了大约2-3%的CPU使用率。一段时间后,通常1-2周应用程序突然开始使用60-70%的CPU使用率,并且线程数似乎保持稳定,不会增加。

我已经在我的代码上运行了我的C#分析器,它归结为以下代码行System.net.Socket.beginReceive()

我正在使用.net async sockets。以下是我的ReceiveCallBack我怀疑当bytesRead不是>0时,我没有处理此案。我应该如何修改下面的功能来正确处理这种情况?

public static void ReadCallback(IAsyncResult ar)
        {
            SocketState tmpRef = null;
            try
            {
                if (ar != null)
                {
                    tmpRef = (SocketState)ar.AsyncState;
                    if (tmpRef != null)
                    {
                        // Read data from the client socket. 
                        int bytesRead = tmpRef.WorkSocket.Client.EndReceive(ar);
                        //Start Reading Again
                        tmpRef.BeginReading(tmpRef._receievCallbackAction);
                        if (bytesRead > 0)
                        {
                            // Check if we have a complete message yet
                            for (var i = 0; i < bytesRead; i++)
                            {
                                if (tmpRef._receiveBuffer[i] == 160)
                                {
                                    var tmpBuffer = new byte[i];
                                    Array.Copy(tmpRef._receiveBuffer, tmpBuffer, i);
                                    //Execute callback
                                    tmpRef._receievCallbackAction(tmpBuffer);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                if (tmpRef != null)
                {
                    //Call callback with null value to indicate a failier
                    tmpRef._receievCallbackAction(null);
                }
            }
        }

完整代码:(抱歉不想弄脏帖子) https://www.dropbox.com/s/yqjtz0r3ppgd11f/SocketState.cs?dl=0

2 个答案:

答案 0 :(得分:1)

问题是如果你没有足够的字节,你的代码会永远等待下一个字节出现。

您需要做的是使messageBuffer在两次通话之间存活并写入,直到您拥有所需的所有数据为止。此外,按照您的代码看起来的方式,您可以在复制所有数据之前覆盖tmpRef._receiveBuffer,如果您正在共享缓冲区,则需要在复制后启动BeginReading

public class SocketState
{
    private readonly List<byte> _messageBuffer = new List<byte>(BufferSize);

    //...

    /// <summary>
    /// Async Receive Callback
    /// </summary>
    /// <param name="ar"></param>
    public static void ReadCallback(IAsyncResult ar)
    {
        SocketState tmpRef = null;
        try
        {
            if (ar != null)
            {
                tmpRef = (SocketState)ar.AsyncState;
                if (tmpRef != null)
                {
                    // Read data from the client socket. 
                    int bytesRead = tmpRef.WorkSocket.Client.EndReceive(ar);
                    if (bytesRead > 0)
                    {
                        //Loop over the bytes we received this read
                        for (var i = 0; i < bytesRead; i++)
                        {   
                            //Copy the bytes from the receive buffer to the message buffer.
                            tmpRef._messageBuffer.Add(tmpRef._receiveBuffer[i]);
                            // Check if we have a complete message yet
                            if (tmpRef._receiveBuffer[i] == 160)
                            {
                                //Copy the bytes to a tmpBuffer to be passed on to the callback.
                                var tmpBuffer = tmpRef._messageBuffer.ToArray();
                                //Execute callback
                                tmpRef._receievCallbackAction(tmpBuffer);
                                //reset the message buffer and keep reading the current bytes read
                                tmpRef._messageBuffer.Clear();
                            }
                        }

                        //Start Reading Again
                        tmpRef.BeginReading(tmpRef._receievCallbackAction);
                    }
                }
            }
        }
        catch (Exception e)
        {
            if (tmpRef != null)
            {
                //Call callback with null value to indicate a failier
                tmpRef._receievCallbackAction(null);
            }
        }
    }

//...

答案 1 :(得分:0)

您正在解释问题发生在1-2周后,这是非常罕见的。 我建议你通过改进readcallback中的异常处理来定位你的研究。

在此异常处理中,事实证明您正在使用null调用callbackAction

也许您应该考虑回答以下问题:

  1. 使用null tmpRef._receievCallbackAction(null);

  2. 调用时,callbackAction的行为如何?
  3. 遇到了什么样的例外?如果是SocketException,可以查看ErrorCode,它可能会给你一个指示

  4. 是否可以转储堆栈跟踪以确切知道它失败的位置?

  5. 其他一些弱点:begin receive使用this作为状态对象。 WorkSocket.Client.BeginReceive(_receiveBuffer, 0, BufferSize, 0, ReadCallback, this);

    因此,这意味着readcallback的线程安全性并不完全得到保证,因为在您尚未处理BeginReading时会发生对_receiveBuffer的调用。