Domanda

Diciamo che voglio leggere una riga da un socket, usando il modulo socket standard:

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? Immagino che dovrei aggiungere un po 'di buffering, comunque:

  

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

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

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

# does this work well, is it efficiently buffered?
s.makefile().readline()
È stato utile?

Soluzione

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

Bloccherà in attesa che il socket contenga dati. In realtà, consentirà solo il blocco delle chiamate di sistema recv () .

file.readline () è un'implementazione bufferizzata efficiente. Non è thread-safe, perché presume che sia l'unico a leggere il file. (Ad esempio bufferando l'immissione imminente.)

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

Sarebbe bufferizzato se:

  • che avevi chiamato readline (), che legge un buffer completo

  • la fine della riga era prima della fine del buffer

In questo modo lasciando i dati nel buffer. In caso contrario, il buffer non viene generalmente riempito eccessivamente.

L'obiettivo della domanda non è chiaro. se hai bisogno di vedere se i dati sono disponibili prima di leggere, puoi selezionare () o impostare il socket in modalità non bloccante con s.setblocking (False) . Quindi, le letture torneranno vuote, anziché bloccate, se non ci sono dati di attesa.

Stai leggendo un file o socket con più thread? Metterei un solo lavoratore a leggere il socket e ad alimentare gli articoli ricevuti in una coda per gestirli con altri thread.

Suggerisci consulenza Python Socket Fonte del modulo e Fonte C che effettua le chiamate di sistema .

Altri suggerimenti

Se ti preoccupi delle prestazioni e controlli completamente la presa (ad esempio non lo passi in una libreria) quindi prova a implementarlo il tuo buffering in Python - Python string.find e string.split e simili 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 si prevede che il payload sia costituito da righe che non sono troppo grandi, che dovrebbero funzionare abbastanza velocemente, ed evitare di saltare attraverso troppi livelli di funzione chiama inutilmente. Sarei interessante nel saperlo come questo si confronta con file.readline () o usando socket.recv (1).

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 = []
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top