與select()
一起使用SSL套接字並不像起初看起來那麼直截了當。雖然它們可以正常工作,但是當你給它一個不會拋出錯誤的時候,如果你只是像正常的套接字一樣使用它們,那麼遲早你肯定會碰到一些奇怪的東西。
由於select()
需要一個文件描述符,它將獲得原始套接字。但即使原始套接字變得可讀,這並不意味着您將從SSL套接字中獲取數據。您需要使用非阻塞套接字(無論如何,在使用select()
時都是一個好主意),如果拋出SSL_ERROR_WANT_READ
(相當於EWOULDBLOCK
的SSL),則忽略它。
另一個問題是,如果你在另一端寫入2048字節的連接,你的末端將返回select()
。但是,如果您只從SSL套接字讀取1024個字節,那麼SSL套接字可能會在內部讀取更多數據,並且即使將有更多數據要讀取,也可能會使連接死鎖,下一個select()
將不會返回。這是因爲原始套接字(select()
正在使用的套接字)沒有任何數據,因爲它已經在SSL套接字的緩衝區中。
想到的第一個解決方案是讀取更多數據,直到讀取拋出SSL_ERROR_WANT_READ
,從而清空緩衝區。然而,如果另一端產生的數據比你能處理它的速度快,這將最終導致你的所有其他連接中斷,直到這個連接完成數據的生成。
您可以通過調用sslsock.pending()
來查看SSL套接字持有多少緩衝數據。那麼更好的方法是首先對一定數量的數據進行一次讀取,檢查未決數據的數量,並對該數據量進行第二次讀取,從而清空緩衝區而不會導致更多的讀取。
該名男子頁SSL_pending()
(幕後的C函數)也這樣說:
SSL_pending()考慮到從當前正在處理的TLS/SSL記錄只有字節(如果有的話)。如果設置了SSL對象的read_ahead標誌,則可能已經讀取了包含更多TLS/SSL記錄的附加協議字節;這些都是由SSL_pending()忽略
據我所知,這意味着如果read_ahead
設置,你需要重複第二步,直到SSL_pending()
返回0。我敢肯定,Python不設置read_ahead
,但最好不要抱歉,所以我在示例代碼中包含了循環。
我沒那麼熟悉,但這樣的事情應該工作:
# Put the SSL socket to non-blocking mode
sslsock.setblocking(0)
while True:
r, w, e = select.select([sslsock], [], [])
if sslsock in r:
try:
data = sslsock.recv(1024)
except ssl.SSLError as e:
# Ignore the SSL equivalent of EWOULDBLOCK, but re-raise other errors
if e.errno != ssl.SSL_ERROR_WANT_READ:
raise
continue
# No data means end of file
if not data:
break
# Drain the SSL socket's internal buffer.
# If you want to remove the loop, make sure you don't call recv()
# with a 0 length, since that could cause a read to the raw socket.
data_left = sslsock.pending()
while data_left:
data += sslsock.recv(data_left)
data_left = sslsock.pending()
# Process the data
process(data)
,如果你發佈了代碼示例這將有助於(降低其基本核心)。 Python 2.6'ssl'模塊的文檔指出將wrap_socket()返回的套接字傳遞給select()應該可以工作。 – 2010-07-10 14:38:30
是的,我真的搞定了,我不太清楚我做錯了什麼,但我想這是事情。但感謝您的答案。 – Andreas 2010-07-12 10:03:11