2009-10-30 61 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非阻塞套接字喜歡養。