如何确保 WSASend() 将发送数据?
How to make sure that WSASend() will send the data?
WSASend()
会立即return 是否发送数据。但是如何确保数据将被发送,例如我的 UI 中有一个按钮,按下时会发送 "Hello World!"
。现在我想确保当用户点击这个按钮时 "Hello World!"
会在某个时候发送,但是 WSASend()
可以 return WSAEWOULDBLOCK
表示数据不会被发送已发送,所以我应该将 WSASend()
包含在一个循环中,该循环直到 WSASend()
returns 0
(成功)才退出。
注意:我正在使用IOCP。
should I enclose WSASend() in a loop that does not exit until
WSASend() returns 0 (success)
错误..不!
让 UI 发出重叠的 WSASend 请求,用 buffer/s 和 OVERLAPPED/s 完成。如果出现奇迹,它确实 return 立即成功(而且我从未见过),那你就很好了。
如果,(when:),它 returns WSA_IO_PENDING,你不能在你的 UI 按钮处理程序中做任何事情,因为 GUI 事件处理程序不能等待。图形 UI 是状态机 - 您必须退出按钮处理程序并 return 以提示方式进入消息输入队列。如果你愿意,你可以做一些 GUI 的事情。也许禁用 'Send' 按钮,或将一些 'Message sent' 文本添加到备忘录组件。就是这样 - 然后你必须退出。
一段时间后,成功完成通知(或失败通知)将发布到 IOCP 完成队列,处理程序线程将获取它。使用 PostMessage、QueueUserAPC 或类似的线程间通信机制向 'something' 发出信号(例如,原始 WSASend 中使用的缓冲区对象),返回到 UI 线程,以便它可以接收 action/s 在 returned 结果上,例如。重新启用 'Send' 按钮。
是的,它看起来很乱,但这是唯一有效的方法。
其他方法 - 轮询循环、Application.DoEvents、计时器等都是可怕的障碍。
Overlapped Socket I/O
If an overlapped operation completes immediately, WSASend returns a value of zero and the lpNumberOfBytesSent parameter is updated with the number of bytes sent. If the overlapped operation is successfully initiated and will complete later, WSASend returns SOCKET_ERROR and indicates error code WSA_IO_PENDING.
...
The error code WSA_IO_PENDING indicates that the overlapped operation has been successfully initiated and that completion will be indicated at a later time. Any other error code indicates that the overlapped operation was not successfully initiated and no completion indication will occur.
...
因此,如 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;
}
WSASend()
会立即return 是否发送数据。但是如何确保数据将被发送,例如我的 UI 中有一个按钮,按下时会发送 "Hello World!"
。现在我想确保当用户点击这个按钮时 "Hello World!"
会在某个时候发送,但是 WSASend()
可以 return WSAEWOULDBLOCK
表示数据不会被发送已发送,所以我应该将 WSASend()
包含在一个循环中,该循环直到 WSASend()
returns 0
(成功)才退出。
注意:我正在使用IOCP。
should I enclose WSASend() in a loop that does not exit until WSASend() returns 0 (success)
错误..不!
让 UI 发出重叠的 WSASend 请求,用 buffer/s 和 OVERLAPPED/s 完成。如果出现奇迹,它确实 return 立即成功(而且我从未见过),那你就很好了。
如果,(when:),它 returns WSA_IO_PENDING,你不能在你的 UI 按钮处理程序中做任何事情,因为 GUI 事件处理程序不能等待。图形 UI 是状态机 - 您必须退出按钮处理程序并 return 以提示方式进入消息输入队列。如果你愿意,你可以做一些 GUI 的事情。也许禁用 'Send' 按钮,或将一些 'Message sent' 文本添加到备忘录组件。就是这样 - 然后你必须退出。
一段时间后,成功完成通知(或失败通知)将发布到 IOCP 完成队列,处理程序线程将获取它。使用 PostMessage、QueueUserAPC 或类似的线程间通信机制向 'something' 发出信号(例如,原始 WSASend 中使用的缓冲区对象),返回到 UI 线程,以便它可以接收 action/s 在 returned 结果上,例如。重新启用 'Send' 按钮。
是的,它看起来很乱,但这是唯一有效的方法。
其他方法 - 轮询循环、Application.DoEvents、计时器等都是可怕的障碍。
Overlapped Socket I/O
If an overlapped operation completes immediately, WSASend returns a value of zero and the lpNumberOfBytesSent parameter is updated with the number of bytes sent. If the overlapped operation is successfully initiated and will complete later, WSASend returns SOCKET_ERROR and indicates error code WSA_IO_PENDING.
...
The error code WSA_IO_PENDING indicates that the overlapped operation has been successfully initiated and that completion will be indicated at a later time. Any other error code indicates that the overlapped operation was not successfully initiated and no completion indication will occur.
...
因此,如 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;
}