我正在寻找一种方法,当套接字变得可读/可写时(即下一个send / recv将立即完成),在I / O完成端口上获取信号。基本上我想要id
的重叠版本。
(是的,我知道对于许多应用程序来说,这是不必要的,您可以继续发出重叠的WSASelect
调用。但在其他应用程序中,您希望延迟生成要发送到最后一刻的消息,正如所讨论的那样e.g. here。在这些情况下,做(a)等待套接字可写,(b)生成下一条消息,(c)发送下一条消息是有用的。)
到目前为止,我能够提出的最佳解决方案是生成一个线程,只是为了调用send
然后调用select
,这是非常糟糕的,并不是特别可扩展......是吗?有更好的方法吗?
答案 0 :(得分:1)
为了检测套接字是否可读,事实证明存在一个没有文档但却众所周知的民间传说:你可以发出一个"零字节读取",即重叠的{{1}使用零字节接收缓冲区,直到有一些数据要读取才能完成。对于试图从大量空闲套接字同时读取的服务器,这有been recommended,以避免内存使用问题(显然IOCP接收缓冲区被固定到RAM中)。可以在WSARecv
源代码中看到此技术的示例。它们还有一个额外的改进,即使用UDP套接字,它们发出一个带有libuv
集的零字节接收。 (这很重要,因为没有该标志,零字节接收将消耗一个数据包,将其截断为零字节。)MSDN声称您不能将MSG_PEEK
与重叠的I / O组合,但显然它适合他们......
当然,这只是答案的一半,因为仍然存在检测可写性的问题。
可能是类似的"零字节发送"技巧会起作用吗? (直接用于TCP,并在UDP套接字上添加MSG_PEEK
标志,以避免实际发送零字节数据包。)实验上,我已经检查过尝试对非零字节发送可写的非阻塞TCP套接字返回MSG_PARTIAL
,这是一个很有希望的标志,但我还没有尝试过重叠的I / O.我最终会解决它并更新这个答案;或者如果有人想先尝试并发布他们自己的综合答案,那么我可能会接受它: - )
答案 1 :(得分:1)
事实证明这是可能的!
基本上,诀窍是:
WSAIoctl
SIO_BASE_HANDLE
浏览所有“分层服务提供商” DeviceIoControl
向AFD驱动程序提交对基本句柄的AFD_POLL
请求(这是select
内部执行的操作)有很多并发症可能值得我们理解,但最终,以上内容应该可以在实践中起作用。这应该是一个私有API,但是libuv使用它,并且MS的兼容性策略意味着它们永远不会破坏libuv,所以很好。有关详细信息,请从以下消息开始阅读线程:https://github.com/python-trio/trio/issues/52#issuecomment-424591743