4

我在 C++ CLI 中为使用 winsock 的服务器编写了一个套接字。套接字使用异步方法来发送、接收和接受连接。在生产环境中实现我的套接字后,发送函数停止工作,给我错误 WSAEWOULDBLOCK。根据我在网上的研究,这意味着套接字 IO 的网络缓冲区已满或网络太忙而无法进行我的操作。但是,我还没有看到任何可以解决这个问题的具体解决方案。我的临时解决方案是围绕 WSASend 函数创建一个 do-while 循环,使线程休眠 X 数量的 MS,然后重试。这导致了比以前的套接字(.NET 套接字类)高得多的延迟和较大的延迟峰值。

我发送数据的代码如下:

void Connectivity::ConnectionInformation::SendData(unsigned char data[], const int length)
{
    if (isClosed || sendError)
        return;

    Monitor::Enter(this->syncRoot);

    try
    {
        sendInfo->buf = (char*)data;
        sendInfo->len = length;

        do
        {
            state = 0;
            if (WSASend(connection, sendInfo, 1, bytesSent, 0, NULL, NULL) == SOCKET_ERROR)
            {
                state = WSAGetLastError();
                if (state == WSAEWOULDBLOCK)
                {
                    Thread::Sleep(SleepTime);
                    //Means the networking is busy and we need to wait a bit for data to be sent
                    //Might wanna decrease the value since this could potentially lead to lagg
                }
                else if (state != WSA_IO_PENDING)
                {
                    this->sendError = true;
                    //The send error bool makes sure that the close function doesn't get called
                    //during packet processing which could cause a lot of null reffernce exceptions.
                }
            }
        }
        while (state == WSAEWOULDBLOCK);
    }
    finally
    {
        Monitor::Exit(this->syncRoot);
    }
}

有没有办法使用例如 WSAEventSelect 方法以便在我能够发送数据时获得回调?从 MSDN 上的文档中可以看出,等待数据方法也可能会陷入此错误。有人有解决这个问题的方法吗?

4

2 回答 2

3

错误代码WSAEWOULDBLOCK表示您尝试对非阻塞套接字进行操作,但操作无法立即完成。这不是真正的错误——这意味着您可以稍后重试或安排异步 IO(不会失败)。但这并不是你想要的。让我解释:

您应该以以下两种方式之一使用套接字:

  1. 同步,阻塞。
  2. 异步、非阻塞、基于回调的。

您将两者混合在一起,这会使您变得最糟糕。您创建了一个非阻塞套接字并以可能阻塞的方式使用它。

唉,我没有资格为本地代码套接字提供最佳实践。我建议您阅读WSASend 的所有文档,因为它们似乎解释了所有这些。

现在,为什么还会存在这个奇怪的错误代码?这是一个性能优化。您可以推测性地尝试同步发送(非常快)。只有当它失败时,你才应该安排一个异步 IO。如果您不需要该优化(您不需要),请不要这样做。

于 2012-12-23T13:04:36.873 回答
1

正如@usr 所说,我需要将 LPWSAOVERLAPPED 或 LPWSAOVERLAPPED_COMPLETION_ROUTINE 设置为一个值,以使操作非阻塞。但是,经过测试,我发现我不需要 LPWSAOVERLAPPED 对象才能调用完成例程。MSDN 上的 WSASend 函数文档也提到,如果重叠对象和完成例程为 NULL,则套接字将表现为阻塞套接字。

谢谢,祝大家圣诞快乐!:)

于 2012-12-25T12:11:27.490 回答