如何确保 WSASend() 将发送数据?

Posted

技术标签:

【中文标题】如何确保 WSASend() 将发送数据?【英文标题】:How to make sure that WSASend() will send the data? 【发布时间】:2015-02-26 20:27:46 【问题描述】:

WSASend() 将立即返回是否发送数据。但是如何确保数据将被发送,例如我的 UI 中有一个按钮,按下时将发送"Hello World!"。现在我想确保当用户单击此按钮时,"Hello World!" 将在某个时候发送,但WSASend() 可能返回WSAEWOULDBLOCK,表示不会发送数据,所以我应该将WSASend() 括在在WSASend() 返回0(成功)之前不会退出的循环。

注意:我正在使用 IOCP。

【问题讨论】:

由于您使用 IOCP,您应该已经知道(与任何其他 IOCP 操作一样)WSASend() 将获取整个数据并在发送完全完成时以 IOCP 完成状态通知您,即使发送立即完成(除非您使用 SetFileCompletionNotificationModes(FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) 禁用它)。 WSAEWOULDBLOCK 不适用于 IOCP,而是使用 ERROR_IO_PENDING/WSA_IO_PENDING 对,异步操作(无论是 IOCP、重叠事件还是完成回调模型)使用 WSA_IO_PENDING 而不是 WSAEWOULDBLOCK。 WSAEWOULDBLOCK 仅适用于非阻塞套接字。 但 MSDN 提到 WSAEWOULDBLOCK:“重叠的套接字:有太多未完成的重叠 I/O 请求。” 这不适用于您,因为希望您一次只有一个未完成的 IO。这是做事的标准方式。 【参考方案1】:

我是否应该将 WSASend() 包含在一个不会退出的循环中,直到 WSASend() 返回 0(成功)

错误..不!

让 UI 发出一个重叠的 WSASend 请求,包括 buffer/s 和 OVERLAPPED/s。如果,由于某种奇迹,它确实立即返回成功,(我从未见过),你很好。

如果,(when:),它返回 WSA_IO_PENDING,你就不能在你的 UI 按钮处理程序中做任何事情,因为 GUI 事件处理程序不能等待。图形 UI 是状态机 - 您必须退出按钮处理程序并以提示方式返回消息输入队列。如果你愿意,你可以做一些 GUI 的东西。也许禁用“发送”按钮,或将一些“已发送消息”文本添加到备忘录组件。就是这样 - 然后你必须退出。

一段时间后,成功完成通知(或失败通知)将发布到 IOCP 完成队列,并且处理程序线程将获取它。使用 PostMessage、QueueUserAPC 或类似的线程间通信机制将“某事”(例如,原始 WSASend 中使用的缓冲区对象)发回给 UI 线程,以便它可以对返回的结果采取行动,例如。重新启用“发送”按钮。

是的,它可以被看作是混乱的,但这是你能做到的唯一有效的方法。

其他方法 - 轮询循环、Application.DoEvents、计时器等都是可怕的障碍。

【讨论】:

"或失败通知" IO操作启动后会失败吗? @user4592590 当然 - 它是异步的。【参考方案2】:

重叠的套接字 I/O

如果重叠操作立即完成,WSASend 将返回零值,并且 lpNumberOfBytesSent 参数会更新为发送的字节数。如果重叠操作成功启动并稍后完成,则 WSASend 返回 SOCKET_ERROR 并指示错误代码 WSA_IO_PENDING。

...

错误代码 WSA_IO_PENDING 表示已成功启动重叠操作,稍后将指示完成。任何其他错误代码表示重叠操作未成功启动,不会出现完成指示。

...

所以正如docs 中演示的那样,您不需要包含在循环中,只需检查SOCKET_ERROR 并且如果最后一个错误不等于WSA_IO_PENDING,则一切正常:

rc = WSASend(AcceptSocket, &DataBuf, 1,
             &SendBytes, 0, &SendOverlapped, NULL);
if ((rc == SOCKET_ERROR) &&
    (WSA_IO_PENDING != (err = WSAGetLastError()))) 
    printf("WSASend failed with error: %d\n", err);
    break;

【讨论】:

是的 - 我只是将我的答案从 WSAEWOULDBLOCK 编辑为 WSA_IO_PENDING。谢谢,点个赞吧:)

以上是关于如何确保 WSASend() 将发送数据?的主要内容,如果未能解决你的问题,请参考以下文章

IOCP 服务器并使用单个 wsasend 发送数据

我应该处理 WSASend() 可能不会发送所有数据的事实吗?

如何避免使用函数调用 WSAsend 将多个缓冲区组合成一个 UDP 数据包?

当没有可用的目的地时,UDP 套接字的 WSASend 触发 FD_READ

在重叠模式下使用 WSASend 时,我应该何时释放缓冲区?

readv()、writev()、WSARecv()、WSASend()