我正在使用异步方法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秒的价值是永远不会超时的。但这似乎有点矫枉过正,我相信有一个不同的根本原因。关于为什么会发生这种情况的任何想法?
答案 0 :(得分:0)
不幸的是,没有足够的代码来完全诊断它。你甚至没有展示sendTimeoutflag
的声明。该示例不是自包含的,因此无法对其进行测试。你还不清楚到底发生了什么(比如你刚刚得到超时,你是否完成了发送并仍然超时,是否还会发生其他事情?)。
那就是说,我在代码中看到至少一个严重的错误,即你使用sendTimeoutflag
。 SendCallback()
方法将此标志设置为1,但立即将其重新设置为0(此时没有Interlocked.CompareExchange()
的保护)。只有在将值设置为0之后才会处理计时器。
这意味着即使成功完成回调,超时计时器也几乎无法保证并且无论如何都要关闭客户端对象。
您可以通过将作业sendTimeoutflag = 0;
移动到实际完成发送操作后的某个点来解决此特定问题,例如:在回调方法的最后。即使这样,只有当你采取措施确保计时器回调不能执行超过该点时(例如等待计时器的处理完成)。
请注意,即使修复了该特定问题,您仍可能有其他错误。坦率地说,目前尚不清楚为什么要首先暂停。也不清楚为什么要使用无锁代码来实现超时逻辑。更常规的锁定(即Monitor
- 基于lock
语句)将更容易正确实现,并且可能不会造成明显的性能损失。
我同意这样的建议:使用async / await模式而不是显式处理回调方法会更好地服务(但当然这意味着使用更高级别的I / O对象,因为Socket没有假设async / await)。