2009-10-30 36 views
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) 

片段的在各个地方的意见在巧舌如簧源,和其他地方提到,在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平台上类似的2个错误代码。换句话说,我越来越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. 
+0

关于你从手册中引用的内容:它不会阻塞一定数量的字节,是正确的。但是......在成功运营的最后阶段,您仍然可以获得EAGAIN。它并没有告诉你这些字节是多少。因此,对这些事件之一的适当反应是成功读取或写入尽可能多的数据,然后在用尽该数字时获取EAGAIN(也称为EWOULDBLOCK)。 – asveikau 2009-11-21 00:55:27

+0

@asveikau:EAGAIN仅适用于非阻塞套接字。如果select()返回阻塞套接字已准备好写入,它将立即接收尽可能多的字节并返回该数字。 – 2009-11-21 02:23:34

+0

@Anacrolix我认为我们在这里讨论write()。如果你在阻塞套接字上选择(),并且它告诉你是时候写了,那么你的write()比可以立即发送的更多,它会被阻塞。此外,在阅读的案例中,既然你提到它......首先我不认为你写的是真的。但假设它是......你怎么知道可以读的上限?只需传入一个大缓冲区?这没有意义。最好在循环中读取(),而不要受到任意大小的限制。 – asveikau 2009-11-21 03:59:18

回答

1

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

当我写这样的程序我倾向于做到以下几点:

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

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

  • poll()(或同等学历)发出信号,你就可以编写,发行写入。如果你EAGAIN/EWOULDBLOCK,删除您从缓冲区写成功的字节,等待你得到暗示下一次。如果你成功地写出了整个缓冲区,然后停止询问POLLOUT所以你不要虚假醒来。

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

我不知道如何与阻止套接字您所需的方法会工作。你一直在“醒来”,因为你在写作的时候要求把你唤醒。你只想指定当你有数据写入时......但是,当它唤醒你时,系统不会真正告诉你你可以写入多少数据而不会阻塞,所以这是一个很好的理由非阻塞I/O。

+1

是的,我想不同的是,即使窗口不是,窗口也会返回该套接字是可写的。这不是我想要的行为,因为写入它然后失败,然后我立即再次获得相同的IO_OUT事件,再次失败......等 – 2009-11-14 13:44:06

+0

,这不是我使用WSAEventSelect()的经验。也许还有别的你遗漏的东西?也许更多的代码可以释放更多的光线 – asveikau 2009-11-14 22:31:44

+0

我已更新我的问题,指出EAGAIN/EWOULDBLOCK附近存在明显的误解。 – 2009-11-15 08:36:31

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); 
+0

@Nicholaz,这是我想要实现的。正如你所描述的那样,'io_add_watch()'调用应该与'select()'等效。在你的例子中'howmany'正在为我返回'-1'。 – 2009-11-19 22:48:39

+1

再次。您无法避免使用poll(),select()或WaitForMultipleObjects()正确处理EAGAIN。即使poll()或select()说,在套接字的输出缓冲区中有一些空间,但它没有说明有多少空间。这意味着你不知道你能写多少。这就是为什么你通常不使用poll()或select()来阻塞IO--整个过程就是为了避免阻塞。而在非阻塞IO的情况下,当这个内部套接字缓冲区填满时,你会得到EAGAIN。这不是一个错误。这是一个正确的用法。 – 2009-11-20 08:08:24

+0

@tomekszpakowicz:引用'的手册页中选择()'('轮询()'和朋友都非常相似,但更多的平台专用):“文件描述符被认为是准备是否可以执行相应的I/O操作(例如,读取(2))而没有阻塞。“。请在声明正确用法之前请使用RTFM。 – 2009-11-20 10:53:15

1

GIO包含GSocket,自“2.22”起一个“低级网络套接字对象”。但是,这还没有被移植到pygobject on Windows

+0

经过测试,现在它已被移植到Windows,似乎GSocket没有任何区别。 – 2011-01-15 16:58:35

0

你可以使用Twisted,其中包括support for GTK(甚至在Windows上),并且将处理所有的various错误情况在Windows非阻塞套接字喜欢养。