Pergunta

Vamos dizer que eu quero ler uma linha de um socket, usando o módulo socket padrão:

def read_line(s):
    ret = ''

    while True:
        c = s.recv(1)

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

    return ret

O que exatamente acontece em s.recv(1)? Será que vai emitir um sistema de chamada de cada vez? Acho que eu deveria adicionar um pouco de tamponamento, de qualquer maneira:

Para melhor partida com hardware e rede realidades, o valor de bufsize deve ser um relativamente pequena potência de 2, por exemplo, 4096.

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

Mas não parece fácil escrever buffer eficiente e thread-safe. E se eu usar file.readline()?

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

Solução

A chamada recv() é tratado diretamente chamando a função de biblioteca C.

Ele irá bloquear aguarda o socket ter dados. Na realidade, ele só vai deixar o bloco de chamada de sistema recv().

file.readline() é uma implementação tamponada eficiente. Não é threadsafe, porque presume que é o único a leitura do arquivo. (Por exemplo pelo tamponamento próxima entrada.)

Se você estiver usando o objeto de arquivo, cada read() tempo é chamado com um argumento positivo, o código subjacente irá recv() apenas a quantidade de dados solicitados, a menos que ele já está tamponada.

Seria tamponada se:

  • Você tinha chamado readline (), que lê um buffer cheio

  • o fim da linha era antes do final do tampão

Assim, deixando os dados no buffer. Caso contrário, o tampão não é geralmente muito cheio.

O objetivo da questão não é clara. se você precisa ver se há dados disponíveis antes de ler, você pode select() ou definir o soquete para o modo não bloqueante com s.setblocking(False). Em seguida, lê-se voltará vazia, em vez de bloquear, se não houver dados de espera.

Você está lendo um arquivo ou soquete com vários segmentos? Gostaria de colocar um único trabalhador na leitura do soquete e alimentando itens recebidos em uma fila para a manipulação por outros threads.

Sugerir consultar Python soquete módulo de fonte e C fonte que faz com que o sistema de chamadas .

Outras dicas

Se você está preocupado com o desempenho e controlar a tomada completamente (Você não está passando-lo em uma biblioteca, por exemplo), em seguida, tentar implementar seu próprio tampão em Python - Python string.find e string.split e tal lata ser incrivelmente 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

Se você espera que a carga consistir em linhas que não são muito grande, que deve ser executado muito rápido, e evitar saltar através de muitas camadas de função chama desnecessariamente. Eu seria interessante saber como isso se compara a file.readline () ou 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top