2009-05-04 11 views
14

Digamos que quiero leer una línea desde un socket, utilizando el módulo estándar socket:tomas Python búfer

def read_line(s): 
    ret = '' 

    while True: 
     c = s.recv(1) 

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

    return ret 

Lo que ocurre exactamente en s.recv(1)? ¿Emitirá una llamada al sistema cada vez? Creo que debería añadir un poco de amortiguación, de todos modos:

Para obtener el mejor partido con las realidades de hardware y de red, el valor de bufsize debería ser una parte relativamente pequeña potencia de 2, por ejemplo, 4096.

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

Pero no parece fácil escribir un búfer eficiente y seguro para subprocesos. ¿Qué pasa si uso file.readline()?

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

"¿Se emitirá una llamada al sistema cada vez?" ¿Por qué importa esto? –

+6

Porque las llamadas al sistema son lentas. Es mejor buscar una gran cantidad de datos (si están disponibles) y luego procesarlos. Ahora sé que Python no es especialmente rápido, y tal vez esto realmente no importa. Pero la documentación dice que es mejor leer en trozos grandes de todos modos. –

+7

Tenga en cuenta que la construcción de una cadena con '+ =' es un no-no, ya que es potencialmente cuadrática, mientras que la construcción de una lista usando anexar el uso de 'str.join' al final es siempre lineal. –

Respuesta

18

La llamada a recv() se maneja directamente llamando a la función de biblioteca C.

Bloqueará la espera de que el socket tenga datos. En realidad, solo dejará que el bloque de llamadas del sistema recv().

file.readline() es una implementación eficiente de búfer. No es threadsafe, ya que supone que es el único que lee el archivo. (Por ejemplo almacenando en búfer la próxima entrada)

Si está utilizando el objeto de archivo, cada vez que se llame a read() con un argumento positivo, el código subyacente recv() solo mostrará la cantidad de datos solicitados, a menos que ya esté almacenado.

Se sería compensado si:

  • hubiera llamado readline(), que lee un buffer lleno

  • el final de la línea era antes del final del búfer

Dejando los datos en el búfer. De lo contrario, el búfer generalmente no está sobrellenado.

El objetivo de la pregunta no está claro. si necesita ver si hay datos disponibles antes de leerlos, puede select() o configurar el socket en el modo sin bloqueo con s.setblocking(False). Luego, las lecturas volverán vacías, en lugar de bloquearse, si no hay datos en espera.

¿Está leyendo un archivo o socket con varios subprocesos? Pondría a un solo trabajador leyendo el zócalo y alimentando los artículos recibidos en una cola para su manejo por otros hilos.

Sugerir consulta Python Socket Module source y C Source that makes the system calls.

+0

No sé por qué pregunté acerca de la seguridad de las hebras, no las necesito en mi proyecto actual. De hecho, quiero reescribir un programa Java en Python. En Java es fácil obtener una lectura en búfer, y me preguntaba si el módulo de socket de Python proporciona el mismo búfer (de hecho, me pregunto por qué alguien no querría almacenar en búfer y llamar directamente a las llamadas al sistema en su lugar). –

+0

realines() no es en tiempo real. así que es inútil para los servicios TCP interactivos como SMTP, readline parece funcionar sin embargo. – Jasen

22

Si usted está preocupado con el rendimiento y controlar el zócalo por completo (que no está de paso en una biblioteca por ejemplo) y luego intentar implementar su propio búfer en Python - Python string.find y string.split y tal puede sea increíblemente rápido.

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 

Si espera a la carga útil consisten en líneas que no son demasiado grandes, que deben ejecutarse bastante rápido, y evitar saltar a través de demasiadas capas de la función llamadas innecesariamente. Sería interesante saber cómo esto se compara con file.readline() o con 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 = []