BeginSend耗时太长,直到回调

时间:2014-10-21 18:31:07

标签: c# sockets asynchronous

我正在使用异步方法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秒的价值是永远不会超时的。但这似乎有点矫枉过正,我相信有一个不同的根本原因。关于为什么会发生这种情况的任何想法?

1 个答案:

答案 0 :(得分:0)

不幸的是,没有足够的代码来完全诊断它。你甚至没有展示sendTimeoutflag的声明。该示例不是自包含的,因此无法对其进行测试。你还不清楚到底发生了什么(比如你刚刚得到超时,你是否完成了发送并仍然超时,是否还会发生其他事情?)。

那就是说,我在代码中看到至少一个严重的错误,即你使用sendTimeoutflagSendCallback()方法将此标志设置为1,但立即将其重新设置为0(此时没有Interlocked.CompareExchange()的保护)。只有在将值设置为0之后才会处理计时器。

这意味着即使成功完成回调,超时计时器也几乎无法保证并且无论如何都要关闭客户端对象。

您可以通过将作业sendTimeoutflag = 0;移动到实际完成发送操作后的某个点来解决此特定问题,例如:在回调方法的最后。即使这样,只有当你采取措施确保计时器回调不能执行超过该点时(例如等待计时器的处理完成)。

请注意,即使修复了该特定问题,您仍可能有其他错误。坦率地说,目前尚不清楚为什么要首先暂停。也不清楚为什么要使用无锁代码来实现超时逻辑。更常规的锁定(即Monitor - 基于lock语句)将更容易正确实现,并且可能不会造成明显的性能损失。

我同意这样的建议:使用async / await模式而不是显式处理回调方法会更好地服务(但当然这意味着使用更高级别的I / O对象,因为Socket没有假设async / await)。