2009-05-04 88 views
14

比方說,我想讀從插座線,使用標準的socket模塊:Python的插座緩衝

def read_line(s): 
    ret = '' 

    while True: 
     c = s.recv(1) 

     if c == '\n' or c == '': 
      break 
     else: 
      ret += c 

    return ret 

什麼s.recv(1)到底會發生?每次都會發出系統調用嗎?我想我應該增加一些緩衝,反正:

對於硬件和網絡的實際情況,BUFSIZE的價值應該是2的功率相對較小,例如最佳匹配,4096

http://docs.python.org/library/socket.html#socket.socket.recv

但編寫高效且線程安全的緩衝並不容易。如果我使用file.readline()怎麼辦?

# does this work well, is it efficiently buffered? 
s.makefile().readline() 
+0

「它會每次發出系統調用嗎?」爲什麼這很重要? – 2009-05-04 21:04:03

+6

由於系統調用較慢。最好是獲取大量數據(如果可用),然後處理它。現在我知道Python並不是特別快,也許這並不重要。但是文檔說無論如何大塊閱讀都更好。 – 2009-05-06 06:46:38

回答

18

recv()調用直接通過調用C庫函數來處理。

它會阻止等待套接字有數據。實際上它只會讓recv()系統調用阻止。

file.readline()是一種高效的緩衝實現。它不是線程安全的,因爲它假設它是唯一一個讀取文件。 (例如通過緩存即將到來的輸入。)

如果您使用的是文件對象,則每次調用read()時都帶有正參數,但底層代碼將僅請求數據量recv(),除非它已被緩衝。

這將被緩衝,如果:

  • 你曾打電話的ReadLine(),其內容全緩衝

  • 行的末尾是緩衝區結束前

因此將數據留在緩衝區中。否則,緩衝區通常不會被過度填充。

該問題的目標不明確。如果您在閱讀之前需要查看數據是否可用,則可以使用select()或將套接字設置爲非阻塞模式,其格式爲s.setblocking(False)。然後,如果沒有等待數據,讀取將返回空白,而不是阻塞。

您是否正在閱讀一個文件或多線程套接字?我會讓單個工作人員讀取套接字並將接收到的項目放入隊列以供其他線程處理。

建議諮詢Python Socket Module sourceC Source that makes the system calls

22

如果您關心的性能和控制插座完全 (你是不是傳遞到例如庫),然後嘗試實現 自己的緩存在Python - Python的string.find和string.split和這樣能 速度驚人。

def linesplit(socket): 
    buffer = socket.recv(4096) 
    buffering = True 
    while buffering: 
     if "\n" in buffer: 
      (line, buffer) = buffer.split("\n", 1) 
      yield line + "\n" 
     else: 
      more = socket.recv(4096) 
      if not more: 
       buffering = False 
      else: 
       buffer += more 
    if buffer: 
     yield buffer 

如果您期望有效載荷由線 是不是太龐大,應該運行非常快, 並避免通過功能太多層 通話不必要跳躍。我知道 與file.readline()或使用socket.recv(1)的比較會很有趣。

6
def buffered_readlines(pull_next_chunk, buf_size=4096): 
    """ 
    pull_next_chunk is callable that should accept one positional argument max_len, 
    i.e. socket.recv or file().read and returns string of up to max_len long or 
    empty one when nothing left to read. 

    >>> for line in buffered_readlines(socket.recv, 16384): 
    ... print line 
    ... 
    >>> # the following code won't read whole file into memory 
    ... # before splitting it into lines like .readlines method 
    ... # of file does. Also it won't block until FIFO-file is closed 
    ... 
    >>> for line in buffered_readlines(open('huge_file').read): 
    ... # process it on per-line basis 
     ... 
    >>> 
    """ 
    chunks = [] 
    while True: 
    chunk = pull_next_chunk(buf_size) 
    if not chunk: 
     if chunks: 
     yield ''.join(chunks) 
     break 
    if not '\n' in chunk: 
     chunks.append(chunk) 
     continue 
    chunk = chunk.split('\n') 
    if chunks: 
     yield ''.join(chunks + [chunk[0]]) 
    else: 
     yield chunk[0] 
    for line in chunk[1:-1]: 
     yield line 
    if chunk[-1]: 
     chunks = [chunk[-1]] 
    else: 
     chunks = []