如何确保 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() 将发送数据?的主要内容,如果未能解决你的问题,请参考以下文章
我应该处理 WSASend() 可能不会发送所有数据的事实吗?
如何避免使用函数调用 WSAsend 将多个缓冲区组合成一个 UDP 数据包?
当没有可用的目的地时,UDP 套接字的 WSASend 触发 FD_READ