2014-02-09 20 views
1

我的問題類似於python - How select.select() works?。但是,那裏的解決方案並不適合我,因爲我沒有打開()我的文件。相反,它是一個套接字。我找不到任何方法將它設置爲在documentation中無緩衝。如何使用選擇與Python ssl套接字緩衝?

我有一個glib mainloop(使用select),在那裏我註冊了socket的讀取。由於socket.recv()要求我指定接收緩衝區大小,因此讀取比讀取套接字更少的字節並不少見。只要內核緩衝它們,這很好; select仍然會將套接字標記爲「準備好讀取」。但顯然Python也有緩衝區。對於接近數據流末尾的大文件,recv()將讀取其中的一部分,其餘部分將被Python緩存並在我的套接字上選擇不再觸發,直到發送新數據爲止。此時,在新數據之前收到「缺失」數據;沒有數據丟失。

我的問題是:我該如何解決這個問題?有沒有辦法在套接字上禁用Python的緩衝區?如果沒有,有沒有辦法來檢查緩衝區是否爲空,所以我可以確保我不會從我的回調中返回,直到它是?

編輯:

正如評論指出,Python沒有額外的緩存添加到插座,所以這可能不是問題。我無法爲這個問題創建一個最簡單的例子。但是,它似乎可能與使用ssl套接字有關。我忘記了我使用了加密連接;禁用加密似乎解決了這個問題,但我不能接受。所以上面的問題仍然存在,注意緩衝區可能在ssl模塊中實現。

示例代碼來說明問題:

#!/usr/bin/python 

import glib 
import socket 
import ssl 

def cb (fd, cond): 
    print ('data: %s' % repr (s.read (1))) 
    return True 

s = ssl.wrap_socket (socket.create_connection (('localhost', 1234))) 
glib.io_add_watch (s.fileno(), glib.IO_IN, cb) 
glib.MainLoop().run() 

然後用

openssl s_server -accept 1234 -key file.key -cert file.crt 

運行Python程序將建立連接運行服務器。發送多於一個字節的數據將使程序僅打印第一個字節;當發送更多字節時,首先讀取剩餘的塊,然後讀取第一個新字節,然後再次等待。這很容易理解:只要ssl緩衝區中有數據,新的字節不會從內核緩衝區中讀取,因此select會繼續報告它。

+2

我只是檢查['Modules/socketmodule.c',從'2485'的行](http://hg.python.org/cpython/file/3a1db0d2747e/Modules/socketmodule.c#l2485 )'sock_recv'被實現的地方,我可以向你保證python不*做任何緩衝。它會創建一個緩衝區,其大小正好是您用recv指定的大小,並且對系統調用recv的調用會跟蹤剩餘的字節並要求*表示多個字節,而不是更多(參見[this loop]( http://hg.python.org/cpython/file/3a1db0d2747e/Modules/socketmodule.c#l2439)) – Bakuriu

+0

我發現,使用SSL插座時,這是唯一的發生,並添加示例代碼,顯示問題。 –

+0

我的解決方案是增加緩衝區大小。我在代碼中使用了'recv(4096)',但數據實際上是4726字節。將其更改爲'recv(64 * 1024)'解決了緩衝和'select()'問題。 – Lekensteyn

回答

1

調查ssl來源,我發現了一個沒有記錄的函數,它做我想要的:pending()。它可以像這樣使用:

#!/usr/bin/python 

import glib 
import socket 
import ssl 

def cb(fd, cond): 
    print('data: %s' % repr(s.read(1))) 
    while(s.pending()): 
     print('more data: %s' % repr(s.read(1))) 
    return True 

s = ssl.wrap_socket (socket.create_connection(('localhost', 1234))) 
glib.io_add_watch(s.fileno(), glib.IO_IN, cb) 
glib.MainLoop().run() 

這就解決了這個問題。

+0

...爲什麼你要在函數名和參數之間加空格? Python有一個着名的*官方風格指南(參見[PEP8](http://www.python.org/dev/peps/pep-0008/))。當你的代碼應該被其他人讀取時(例如,在這個答案中),你至少應該遵循*。 – Bakuriu

+0

我不知道這個風格指南,所以它不像你想象的那麼出名(說我的n = 1樣本:-P)另外,「官方」部分意味着它是必須的樣式在編寫代碼_for_ Python時使用,以包含在官方發行版中。如果沒有,你可以做任何你喜歡的事情。我同意使用共同的風格是一個好主意,但它並不像聲音那樣強制。 –

+0

PEP8具有更廣的範圍,那麼只是標準庫。例如「這裏提供的指導原則旨在提高代碼的可讀性,並使其在各種Python代碼中保持一致*」。最後一句是指所有開源項目。事實上,像'pylint'或'pychecker'這樣的工具用於做一些靜態分析,還包括檢查PEP8風格。不遵循PEP8時,PyCharm等IDE會自動顯示警告。 PEP8 *是當你想向python社區展示代碼時使用的*樣式。 – Bakuriu