2016-03-18 104 views
1

在Python中,我想在我設置爲非阻塞的套接字上使用socket.connect()。當我嘗試這樣做時,該方法總是拋出一個BlockingIOError。當我忽略錯誤(如下)時,程序按預期執行。在連接後將套接字設置爲非阻塞狀態時,沒有錯誤。當我使用select.select()來確保套接字是可讀或可寫的時,我仍然得到錯誤。如何使用非阻塞套接字連接()?

testserver.py

import socket 
import select 

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.setblocking(0) 

host = socket.gethostname() 
port = 1234 

sock.bind((host, port)) 
sock.listen(5) 

while True: 
    select.select([sock], [], []) 
    con, addr = sock.accept() 
    message = con.recv(1024).decode('UTF-8') 
    print(message) 

testclient.py

import socket 
import select 

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.setblocking(0) 

host = socket.gethostname() 
port = 1234 

try: 
    sock.connect((host, port)) 
except BlockingIOError as e: 
    print("BlockingIOError") 

msg = "--> From the client\n" 

select.select([], [sock], []) 
if sock.send(bytes(msg, 'UTF-8')) == len(msg): 
    print("sent ", repr(msg), " successfully.") 

sock.close() 

終端1

$ python testserver.py 
--> From the client 

終端2

$ python testclient.py 
BlockingIOError 
sent '--> From the client\n' successfully. 

此代碼正常除了BlockingIOError在第一連接()。錯誤的文檔如下所示:Raised when an operation would block on an object (e.g. socket) set for non-blocking operation.

如何正確連接()與套接字設置爲非阻塞?我可以使connect()非阻塞嗎?或者忽略錯誤是否合適?

+0

此鏈接可能會對您有所幫助: http://stackoverflow.com/questions/1205863/how-can-i-get-non-blocking-socket-connects – akash12300

+0

@ Akash1993是的,當我以前看過那個問題時很難將它與我簡單的例子聯繫起來。我想避免asyncio;它似乎矯枉過正。它確實闡明瞭錯誤是因爲連接塊時不應該引發錯誤(以及blockingioerror文檔)。問題是'有沒有辦法讓connect()非阻塞'。 – MikeJava

回答

0

這裏的訣竅是當選擇第一次完成時,然後您需要再次撥打sock.connect在您從connect收到成功的退貨狀態之前,插座纔會連接。

第一個電話後,只需添加這兩條線select完成:

print("first select completed") 
sock.connect((host, port)) 

編輯:
跟進。我錯誤地表示需要額外致電sock.connect。然而,如果您希望在其自己的代碼路徑中處理連接故障,那麼發現原始的非阻塞呼叫connect是否成功是一種好方法。

在C代碼實現這一目標的傳統方法是在這裏解釋:Async connect and disconnect with epoll (Linux)

這就需要調用getsockopt。你也可以在Python中做到這一點,但是你從sock.getsockopt得到的結果是一個bytes對象。如果它代表失敗,則需要將其轉換爲整數errno值,並將其映射到字符串(或異常或將問題傳達給外部世界所需的任何內容)。調用sock.connect再次將errno值映射到適當的例外。

解決方案2: 您也可以簡單地延遲撥打sock.setblocking(0),直到連接完成。

+1

我發佈的代碼按照它應有的方式工作,我編輯了這個問題以使其更加明確。問題是它在第一次連接時拋出的BlockingIOError,而不是連接的成功。如果你檢查我發佈的終端輸出,也許它會更清楚。 – MikeJava

+0

解決方案2絕對正確!我其實在我的問題中提到過它;我想知道在套接字設置爲非阻塞之後是否有辦法做到這一點。 – MikeJava

+0

我不這樣做。你似乎在問:「在我詢問非阻塞行爲後,是否有辦法阻止行爲?」如果你不調用'sock.setblocking(0)',默認行爲是阻塞的。 –