Pregunta

Digamos que quiero leer una línea desde un socket, usando el módulo estándar socket :

def read_line(s):
    ret = ''

    while True:
        c = s.recv(1)

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

    return ret

¿Qué sucede exactamente en s.recv (1) ? ¿Se emitirá una llamada al sistema cada vez? Supongo que debería agregar algo de búfer, de todos modos:

  

Para obtener la mejor coincidencia con las realidades de hardware y red, el valor de bufsize debe ser una potencia relativamente pequeña 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é sucede si uso file.readline () ?

# does this work well, is it efficiently buffered?
s.makefile().readline()
¿Fue útil?

Solución

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

Bloqueará a la espera de que el socket tenga datos. En realidad, solo permitirá el bloqueo de llamadas al sistema recv () .

file.readline () es una implementación eficiente en búfer. No es seguro para subprocesos, porque 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 la cantidad de datos solicitados , a menos que ya esté almacenado.

Se almacenaría en búfer si:

  • que habías llamado readline (), que lee un búfer completo

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

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

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

¿Estás leyendo un archivo o socket con varios subprocesos? Yo pondría a un solo trabajador en la lectura del zócalo y colocando los artículos recibidos en una cola para que otros subprocesos los manejen.

Sugiera consultoría Python Socket Fuente del módulo y C Source eso hace que el sistema llame .

Otros consejos

Si le preocupa el rendimiento y controla el zócalo completamente (no lo está pasando a una biblioteca, por ejemplo) luego intente implementar su propio almacenamiento en búfer en Python - Python string.find y string.split y tales pueden Sé 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 que la carga útil consista en líneas que no son demasiado grandes, que deberían correr bastante rápido, y evita saltar a través de demasiadas capas de función. Llama innecesariamente. Estaría interesado en saber cómo esto se compara 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 = []
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top