2009-05-04 7 views
14

Diciamo che voglio leggere una riga da una presa, utilizzando il modulo standard socket:prese Python buffer

def read_line(s): 
    ret = '' 

    while True: 
     c = s.recv(1) 

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

    return ret 

Cosa succede esattamente in s.recv(1)? Emetterà una chiamata di sistema ogni volta? Credo che dovrei aggiungere un po 'di buffering, in ogni caso:

Per una migliore corrispondenza con hardware e di rete realtà, il valore di bufsize dovrebbe essere una relativamente piccola potenza di 2, per esempio, 4096.

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

Ma non sembra facile scrivere buffer efficiente e thread-safe. Cosa succede se uso file.readline()?

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

"Emetterà una chiamata di sistema ogni volta?" Perché questo importa? –

+6

Perché le chiamate di sistema sono lente. È meglio recuperare una grande quantità di dati (se disponibile), quindi elaborarli. Ora so che Python non è particolarmente veloce, e forse questo non ha molta importanza. Ma la documentazione dice che è comunque meglio leggere da grandi pezzi. –

+7

Nota che costruire una stringa usando '+ =' è un no-no dato che è potenzialmente quadratico, mentre la costruzione di un elenco usando append usando 'str.join' alla fine è sempre lineare. –

risposta

18

La recv() chiamata viene gestita direttamente chiamando la funzione di libreria C.

Bloccherà in attesa che il socket abbia dati. In realtà lascerà semplicemente il blocco di chiamate di sistema recv().

file.readline() è un'implementazione buffer efficiente. Non è protetto da thread, perché presume che sia l'unico a leggere il file. (Ad esempio, con il buffering dell'imminente input.)

Se si utilizza l'oggetto file, ogni volta che read() viene chiamato con un argomento positivo, il codice sottostante sarà recv() solo la quantità di dati richiesta, a meno che non sia già presente nel buffer.

Sarebbe essere tamponato se:

  • aveste chiamato readline(), che legge un buffer pieno

  • alla fine della linea era prima della fine del buffer

Così lasciando i dati nel buffer. In caso contrario, il buffer non è generalmente sovraccarico.

L'obiettivo della domanda non è chiaro. se è necessario verificare se i dati sono disponibili prima della lettura, è possibile impostare select() o impostare il socket in modalità non bloccante con s.setblocking(False). Quindi, le letture restituiscono vuoto, piuttosto che il blocco, se non ci sono dati di attesa.

Stai leggendo un file o un socket con più thread? Metterei un singolo lavoratore a leggere il socket e inserire gli elementi ricevuti in una coda per la gestione da parte di altri thread.

Suggerisci consulenza Python Socket Module source e C Source that makes the system calls.

+0

Non so davvero perché ho chiesto informazioni sulla sicurezza dei thread, non ne ho bisogno nel mio progetto corrente. In effetti, voglio riscrivere un programma Java in Python. In Java è facile ottenere la lettura bufferizzata, e mi chiedevo se il modulo socket di Python fornisce lo stesso buffering (in effetti, mi chiedo perché qualcuno non voglia il buffering e invochi direttamente le chiamate di sistema). –

+0

realines() non è in tempo reale. quindi è inutile per i servizi TCP interattivi come SMTP, tuttavia sembra che readline funzioni. – Jasen

22

Se siete interessati con le prestazioni e controllare la presa completamente (non si passa in una libreria, ad esempio), allora provare l'attuazione il proprio buffer in Python - Python string.find e string.split e tale può essere incredibilmente veloce.

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 

Se vi aspettate che il carico utile a costituiti da linee che non sono troppo grande, che dovrebbe correre abbastanza veloce, ed evitare saltare attraverso troppi strati di funzione chiamate inutilmente. Sarei interessante conoscere in confronto a file.readline() o usando 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 = []