WSAEWOULDBLOCK 处理
Posted
技术标签:
【中文标题】WSAEWOULDBLOCK 处理【英文标题】:WSAEWOULDBLOCK handling 【发布时间】:2012-12-23 10:38:35 【问题描述】:我在 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 上的文档中可以看出,等待数据方法也可能会陷入此错误。有人有解决这个问题的方法吗?
【问题讨论】:
您使用的是哪种异步操作?非阻塞套接字或重叠 I/O?处理多个并发请求的一般架构是什么?单线程/选择/非阻塞套接字?完成例程?活动? 您似乎没有使用异步 IO。为什么你认为你是? 我认为 WSASend 方法默认是异步的。 send 方法是一个发送后忘记的方法,因为我不太关心回调的结果。我正在使用 .NET 线程池等待接收和连接接受的回调:WaitHandle ^ handle = gcnew ManualResetEvent(false); IntPtr handlePointer = 句柄->SafeWaitHandle->DangerousGetHandle(); WSAEventSelect(listener, (void*)handlePointer, FD_ACCEPT); ThreadPool::UnsafeRegisterWaitForSingleObject(handle, callback, nullptr, -1, true);我如何知道我使用的是非阻塞、重叠 IO 还是阻塞套接字? 请将代码附加到您的问题中。在评论中很难读懂。 MSDN 有一个Asynchronous Server Sockets 的示例。因为它只是 .NET(没有 C++、C++ CLI),所以要容易得多。另一种方法是使用多个线程并进行简单的阻塞 I/O(仅在 C# 中)。 【参考方案1】:错误码WSAEWOULDBLOCK
表示您试图对非阻塞套接字进行操作,但操作无法立即完成。这不是真正的错误——这意味着您可以稍后重试或安排异步 IO(不会失败)。但这并不是你想要的。让我解释一下:
您应该通过以下两种方式之一使用套接字:
-
同步、阻塞。
异步、非阻塞、基于回调。
您将两者混为一谈,这会使您变得最糟糕。您创建了一个非阻塞套接字并以可能阻塞的方式使用它。
唉,我没有资格为本地代码套接字提供最佳实践。我建议您阅读所有docs for WSASend,因为它们似乎解释了所有这些。
现在,为什么还会存在这个奇怪的错误代码?这是一个性能优化。您可以推测性地尝试同步发送(非常快)。只有当它失败时,你才应该安排一个异步 IO。如果您不需要该优化(您不需要),请不要这样做。
【讨论】:
我的套接字在 90% 的发送操作中都在工作,根据我在网上阅读的内容,当套接字最初阻塞时,我会在所有发送操作中遇到该错误。 msdn.microsoft.com/en-us/library/windows/desktop/… 说“这个错误是从无法立即完成的非阻塞套接字上的操作返回的,例如当没有数据排队从套接字读取时的接收”我知道我需要再试一次,我只需要一个事件或其他东西正如我在第一篇文章中解释的那样,它告诉我何时能够发送数据。 @Martin,正如我所解释的,如果数据可以排队到内存中的发送队列中,非阻塞模式下的 WSASend 可以成功。如果队列已满,则呼叫失败。你刚才引用的文字对 recv 也有类似的说法(你同意吗?)。您如何看待我坚持使用 (1) 或 (2) 的建议?目前,您没有按照应有的方式使用套接字。如果你做对了,你就不需要一个事件。您只需要调用 WSASend,它就不会失败。 所以从我所见,我需要调用 ioctlsocket 将套接字设置为异步模式。我是否应该在接受连接后调用此方法,是否应该在其中添加任何其他参数而不是空指针?另外,我想坚持使用非阻塞/异步套接字。 @Martin,根据msdn.microsoft.com/en-us/library/windows/desktop/…,您应该将套接字创建为重叠(这与非阻塞不同!)并使用 WSASend 至少具有重叠结构或完成回调之一(我推荐后者)。请注意,您不应该等待任何事情 - 仅依赖回调。这将强制将您的代码翻转过来,使其基于回调。也许您可以切换到同步 IO(为什么不能?)。【参考方案2】:正如@usr 所说,我需要将 LPWSAOVERLAPPED 或 LPWSAOVERLAPPED_COMPLETION_ROUTINE 设置为一个值,以使操作非阻塞。但是,经过测试,我发现我不需要 LPWSAOVERLAPPED 对象才能调用完成例程。 MSDN 上 WSASend 函数的文档中也提到,如果重叠对象和完成例程为 NULL,则套接字将表现为阻塞套接字。
谢谢,祝大家圣诞快乐! :)
【讨论】:
以上是关于WSAEWOULDBLOCK 处理的主要内容,如果未能解决你的问题,请参考以下文章