BeginSend需要很长时间才能回调
本文关键字:回调 长时间 BeginSend | 更新日期: 2023-09-27 18:27:17
我使用的是异步方法BeginSend,我需要某种超时机制。我所实现的对于连接和接收超时都很好,但BeginSend回调有问题。即使是25秒的超时也往往不够,甚至会被超过。这对我来说似乎很奇怪,指向了另一个原因。
public void Send(String data)
{
if (client.Connected)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
client.NoDelay = true;
// Begin sending the data to the remote device.
IAsyncResult res = client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
if (!res.IsCompleted)
{
sendTimer = new System.Threading.Timer(SendTimeoutCallback, null, 10000, Timeout.Infinite);
}
}
else MessageBox.Show("No connection to target! Send");
}
private void SendCallback(IAsyncResult ar)
{
if (Interlocked.CompareExchange(ref sendTimeoutflag, 1, 0) != 0)
{
// the flag was set elsewhere, so return immediately.
return;
}
sendTimeoutflag = 0; //needs to be reset back to 0 for next reception
// we set the flag to 1, indicating it was completed.
if (sendTimer != null)
{
// stop the timer from firing.
sendTimer.Dispose();
}
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
ef.updateUI("Sent " + bytesSent.ToString() + " bytes to server." + "'n");
}
catch (Exception e)
{
MessageBox.Show(e.ToString());
}
}
private void SendTimeoutCallback(object obj)
{
if (Interlocked.CompareExchange(ref sendTimeoutflag, 2, 0) != 0)
{
// the flag was set elsewhere, so return immediately.
return;
}
// we set the flag to 2, indicating a timeout was hit.
sendTimer.Dispose();
client.Close(); // closing the Socket cancels the async operation.
MessageBox.Show("Connection to the target has been lost! SendTimeoutCallback");
}
我测试了长达30秒的超时值。30秒的数值被证明是唯一一个永远不会超时的数值。但这似乎有些过头了,我相信还有另一个根本原因。关于为什么会发生这种情况,有什么想法吗?
不幸的是,没有足够的代码来完全诊断这一问题。您甚至没有显示sendTimeoutflag
的声明。这个例子不是自包含的,所以没有办法测试它。你也不清楚到底发生了什么(例如,你只是得到了超时,你完成了发送但仍然得到了超时吗,还有其他事情发生吗?)。
也就是说,我在代码中看到了至少一个严重的错误,那就是您对sendTimeoutflag
的使用。SendCallback()
方法将此标志设置为1,但它立即将其再次设置回0(这次没有Interlocked.CompareExchange()
的保护)。只有在将值设置为0之后,它才会释放计时器。
这意味着,即使您成功地完成了回调,也几乎可以保证超时计时器不知道,并且无论如何都会关闭客户端对象。
您可以通过将分配sendTimeoutflag = 0;
移动到实际完成发送操作之后的某个点来解决此特定问题,例如在回调方法的末尾。即便如此,也只有当您采取措施确保计时器回调不能在该点之后执行时(例如,等待计时器的处理完成)。
请注意,即使修复了这个特定的问题,您也可能有其他错误。坦率地说,目前还不清楚你为什么要暂停。也不清楚为什么要使用无锁代码来实现超时逻辑。更传统的锁定(即基于Monitor
的lock
语句)将更容易正确实现,并且可能不会造成明显的性能损失。
我同意这样一个建议,即使用async/await模式而不是显式处理回调方法会更好地为您服务(当然,这意味着使用更高级别的I/O对象,因为Socket不假设异步/await)。