我有一个服务器软件,它有一个监听套接字,然后跨越多个套接字(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
答案 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
。
也许您应该考虑回答以下问题:
使用null tmpRef._receievCallbackAction(null);
遇到了什么样的例外?如果是SocketException,可以查看ErrorCode,它可能会给你一个指示
是否可以转储堆栈跟踪以确切知道它失败的位置?
其他一些弱点:begin receive使用this
作为状态对象。
WorkSocket.Client.BeginReceive(_receiveBuffer, 0, BufferSize, 0, ReadCallback, this);
因此,这意味着readcallback的线程安全性并不完全得到保证,因为在您尚未处理BeginReading
时会发生对_receiveBuffer
的调用。