2010-07-06 82 views
4

我有一個使用select.select()的服務器應用程序,現在我試圖向它添加SSL,但是在聽「raw」套接字時出現以下錯誤:在python中選擇和ssl

ValueError: file descriptor cannot be a negative integer (-1) 

所以我想我會使用ssl.wrap_socket在select中返回的ssl流。這樣做,它不會返回任何錯誤,但它也不起作用 - 我不確定問題是什麼,我已經做了大量的研究並遇到類似問題的帖子,但是我找不到解決這個問題呢。

真的很感謝任何幫助。

+3

,如果你發佈了代碼示例這將有助於(降低其基本核心)。 Python 2.6'ssl'模塊的文檔指出將wrap_socket()返回的套接字傳遞給select()應該可以工作。 – 2010-07-10 14:38:30

+0

是的,我真的搞定了,我不太清楚我做錯了什麼,但我想這是事情。但感謝您的答案。 – Andreas 2010-07-12 10:03:11

回答

-3

正如Marius指出的,select.select()適用於SSL套接字,我仍然不知道是什麼導致了我的沉默錯誤,但是當我認爲它是SSL + select()時,我跳過了我的槍。因此這個問題得到了回答。

10

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)