在 Windows 上使用 Glib 监视套接字会将它们置于非阻塞模式
以下代码在 Windows 上无法正常工作(但在 Linux 上可以):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(True)
sock.connect(address)
gobject.io_add_watch(
sock.fileno(),
gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP,
callback)
glib 源中各个位置的注释片段以及其他地方提到在 Windows 中,套接字在轮询期间置于非阻塞模式。因此,回调 self.outgoing_cb
不断被调用,并且写入套接字失败并显示以下错误消息:
[Errno 10035] A non-blocking socket operation could not be completed immediately
在写入之前调用 sock.setblocking(True)
似乎不存在来规避这一点。通过降低轮询的优先级并忽略错误消息,它可以按预期工作,但会抛出很多事件,并消耗大量 CPU。 Windows 中有没有办法绕过这个限制?
更新
我可能会指出,轮询POLLOUT
的全部意义在于,当您进行写入调用时,您不会得到EAGAIN
/ EWOULDBLOCK
。我相信我收到的奇怪错误消息在 Windows 中相当于这两个错误代码。换句话说,当套接字不允许我成功写入时,我会收到 gobject.IO_OUT
事件,并将其置于阻塞模式仍然会给我这个不适当的错误。
另一个更新
在 Linux 上,此功能正常工作,套接字不会切换到非阻塞模式,并且当套接字允许我无阻塞地写入时,我会收到 IO_OUT,或抛出错误。我想在 Windows 下最好地模拟/恢复这个功能。
更多说明
来自 man poll
:
poll() performs a similar task to select(2): it waits for one of a set
of file descriptors to become ready to perform I/O.
POLLOUT
Writing now will not block.
来自 man select
:
A file descriptor is considered ready if it is possible to perform the corre‐
sponding I/O operation (e.g., read(2)) without blocking.
The following code does not work correctly on Windows (but does on Linux):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(True)
sock.connect(address)
gobject.io_add_watch(
sock.fileno(),
gobject.IO_OUT | gobject.IO_ERR | gobject.IO_HUP,
callback)
Snippets of comments in various places in the glib source, and other places mention that in Windows, sockets are put in non-blocking mode during polling. As a result the callback self.outgoing_cb
is constantly called, and writing to the socket fails with this error message:
[Errno 10035] A non-blocking socket operation could not be completed immediately
Calling sock.setblocking(True)
prior to writing does not seem to circumvent this. By lowering the priority of the polling, and ignoring the error message, it works as expected, but throws far to many events, and consumes a lot of CPU. Is there a way around this limitation in Windows?
Update
I might point out, that the whole point of polling for POLLOUT
is that when you make the write call you won't get EAGAIN
/EWOULDBLOCK
. The strange error message that I'm getting, I believe would be the Windows equivalent of those 2 error codes. In other words, I'm getting gobject.IO_OUT
events when the socket will not let me write successfully, and putting it into blocking mode still gives me this inappropriate error.
Another update
On Linux, where this works correctly, the socket is not switched to non-blocking mode, and I receive IO_OUT
, when the socket will let me write without blocking, or throwing an error. It's this functionality I want to best emulate/restore under Windows.
Further notes
From man poll
:
poll() performs a similar task to select(2): it waits for one of a set
of file descriptors to become ready to perform I/O.
POLLOUT
Writing now will not block.
From man select
:
A file descriptor is considered ready if it is possible to perform the corre‐
sponding I/O operation (e.g., read(2)) without blocking.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
执行非阻塞 I/O 是否有问题?如果您使用阻塞 I/O,那么使用轮询循环似乎有点奇怪。
当我编写这样的程序时,我倾向于执行以下操作:
缓冲我想要发送到文件描述符的字节。
仅在所述缓冲区非空时请求
IO_OUT
(或poll()
等效项,POLLOUT
)事件。当
poll()
(或等效函数)发出信号表明您已准备好写入时,发出写入。如果收到EAGAIN
/EWOULDBLOCK
,请从缓冲区中删除成功写入的字节,并等待下一次收到信号。如果您成功写入了整个缓冲区,则停止请求POLLOUT
,这样您就不会错误地唤醒。(我的猜测是 Win32 绑定正在使用 WSAEventSelect< /a> 和
WaitForMultipleObjects()
来模拟poll()
,但结果是相同的...)我不确定您想要的阻塞套接字方法如何工作。你不断地“醒来”,因为你要求在你可以写作的时候叫醒你。你只想指定当你有数据要写入时...但是,当它唤醒你时,系统不会真正告诉你在不阻塞的情况下可以写入多少数据,所以这就是使用非阻塞 I/O 的一个很好的理由。
Is there a problem with doing non-blocking I/O? It seems kind of strange to use polling loops if you're using blocking I/O.
When I write programs like this I tend to do the following:
Buffer the bytes I want to send to the file descriptor.
Only ask for
IO_OUT
(or thepoll()
equivalent,POLLOUT
) events when said buffer is non-empty.When
poll()
(or equivalent) has signaled that you're ready to write, issue the write. If you getEAGAIN
/EWOULDBLOCK
, remove the bytes you successfully wrote from the buffer and wait for the next time you get signaled. If you successfully wrote the entire buffer, then stop asking forPOLLOUT
so you don't spuriously wake up.(My guess is that the Win32 bindings are using WSAEventSelect and
WaitForMultipleObjects()
to simulatepoll()
, but the result is the same...)I'm not sure how your desired approach with blocking sockets would work. You are "waking up" constantly because you asked to wake you up when you can write. You only want to specify that when you have data to write... But then, when it wakes you up, the system won't really tell you how much data you can write without blocking, so that's a good reason to use non-blocking I/O.
GIO 包含 GSocket,自 2.22 起的“低级网络套接字对象”。然而,这尚未移植到Windows 上的 pygobject。
GIO contains GSocket, a "lowlevel network socket object" since 2.22. However this is yet to be ported to pygobject on Windows.
我不确定这是否有帮助(我不熟悉 poll 函数或 MFC 套接字,并且不知道轮询是程序结构的要求),所以对此持保留态度:
但要避免写入时阻塞或 EAGAIN,我们使用 select,即将套接字添加到传递给 select 的写入集中,如果 select() 返回 rc=0,则套接字将立即接受写入...
我们的写入循环在我们的应用程序中使用的是(伪代码):
I'm not sure if this helps (I'm not proficient with the poll function or the MFC sockets and don't know the polling is a requirement of your program structure), so take this with a grain of salt:
But to avoid a blocking or EAGAIN on write, we use select, i.e. add the socket to the write set that is passed to select, and if select() comes back with rc=0 the socket will accept writes right away ...
The write loop we use in our app is (in pseudocode):
您可以使用 Twisted,其中包括 支持 GTK(甚至在 Windows 上),并将处理所有 Windows 上的非阻塞套接字喜欢引发的各种 错误情况。
You could use Twisted, which includes support for GTK (even on Windows) and will handle all the various error conditions that non-blocking sockets on Windows like to raise.