web-dev-qa-db-ja.com

Pythonソケットバッファリング

標準のsocketモジュールを使用して、ソケットから行を読みたいとしましょう:

_def read_line(s):
    ret = ''

    while True:
        c = s.recv(1)

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

    return ret
_

s.recv(1)で正確に何が起こりますか?毎回システムコールを発行しますか?とにかく、バッファリングを追加する必要があると思います。

ハードウェアとネットワークの現実との最適な一致を得るには、bufsizeの値を2の比較的小さいべき乗にする必要があります(例:4096)。

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

しかし、効率的でスレッドセーフなバッファリングを書くのは簡単ではないようです。 file.readline()を使用するとどうなりますか?

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

recv()呼び出しは、Cライブラリ関数を呼び出すことによって直接処理されます。

ソケットがデータを持つのを待つのをブロックします。実際には、recv()システムコールをブロックさせるだけです。

file.readline()は、効率的なバッファ実装です。スレッドセーフではありません。ファイルを読み取るのはそれだけだと想定されているためです。 (たとえば、次の入力をバッファリングすることによって。)

ファイルオブジェクトを使用している場合、read()が正の引数で呼び出されるたびに、基礎となるコードは、すでにバッファリングされていない限り、要求されたデータの量だけrecv()になります。

次の場合にバッファリングされます。

  • バッファ全体を読み取るreadline()を呼び出した

  • 行の終わりがバッファの終わりより前でした

したがって、データをバッファに残します。それ以外の場合、バッファは一般的に過剰に充填されません。

質問の目的は明確ではありません。読み取る前にデータが利用可能かどうかを確認する必要がある場合は、select()またはs.setblocking(False)を使用してソケットをノンブロッキングモードに設定できます。次に、待機中のデータがない場合、読み取りはブロッキングではなく空を返します。

複数のスレッドで1つのファイルまたはソケットを読み取っていますか?私は1人のワーカーにソケットを読み取らせ、他のスレッドで処理するために受信したアイテムをキューに入れました。

コンサルティングを提案 Python Socket Module source および システムコールを作成するCソース

20
Joe Koberg

パフォーマンスに関心があり、ソケットを完全に制御している場合(たとえば、ソケットをライブラリに渡していない場合)、Python-Python string.findおよびstring.splitに独自のバッファリングを実装してみてください。これは驚くほど高速です。

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

ペイロードがそれほど大きくない行で構成されると予想される場合、それはかなり高速に実行され、不必要に多くの層の関数呼び出しを飛び越えないようにします。これがfile.readline()とどのように比較されるのか、またはsocket.recv(1)を使用することに興味があります。

29
Aaron Watters
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 = []
8
alex