3

以下代码在 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在于,当您进行 write 调用时,您将不会得到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.
4

4 回答 4

1

做非阻塞 I/O 有问题吗?如果您使用阻塞 I/O,使用轮询循环似乎有点奇怪。

当我编写这样的程序时,我倾向于执行以下操作:

  • 缓冲我想发送到文件描述符的字节。

  • 仅当所述缓冲区非空时才请求IO_OUT(或poll()等效的)事件。POLLOUT

  • poll()(或等价物)表明您已准备好写入时,发出写入。如果你得到EAGAIN/ EWOULDBLOCK,从缓冲区中删除你成功写入的字节并等待下一次你收到信号。如果您成功写入了整个缓冲区,则停止请求,POLLOUT这样您就不会虚假地醒来。

(我的猜测是 Win32 绑定使用WSAEventSelectWaitForMultipleObjects()模拟poll(),但结果是一样的......)

我不确定您希望使用阻塞套接字的方法如何工作。你一直在“醒来”,因为你要求在你可以写作的时候叫醒你。您只想指定当您有数据要写入时......但是,当它唤醒您时,系统不会真正告诉您可以在没有阻塞的情况下写入多少数据,因此这是使用非阻塞的一个很好的理由阻塞 I/O。

于 2009-11-14T02:28:29.323 回答
1

GIO包含GSocket,自 2.22 以来的“低级网络套接字对象”。但是,这尚未移植到Windows 上的 pygobject

于 2010-10-31T08:46:25.843 回答
0

我不确定这是否有帮助(我不精通轮询函数或 MFC 套接字,也不知道轮询是您的程序结构的要求),因此请谨慎对待:

但是为了避免写入时阻塞或 EAGAIN,我们使用 select,即将套接字添加到传递给 select 的写入集中,如果 select() 返回 rc=0,套接字将立即接受写入...

我们在应用程序中使用的写循环是(在伪代码中):

set_nonblocking.
count= 0.
do {
   FDSET writefds;
   add skt to writefds.
   call select with writefds and a reaonsable timeout.
   if (select fails with timeout) {
       die with some error;
   } 

   howmany= send(skt, buf+count, total-count).
   if (howmany>0) {
       count+= howmany.
   }
} while (howmany>0 && count<total);
于 2009-11-19T13:43:35.007 回答
0

您可以使用Twisted,它包括对 GTK 的支持(甚至在 Windows 上),并将处理 Windows上的非阻塞套接字喜欢引发的所有各种错误情况。

于 2011-05-09T16:41:12.533 回答