2009-05-04 86 views
14

比方说,我想读从插座线,使用标准的socket模块:Python的插座缓冲

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() 
+0

“它会每次发出系统调用吗?”为什么这很重要? – 2009-05-04 21:04:03

+6

由于系统调用较慢。最好是获取大量数据(如果可用),然后处理它。现在我知道Python并不是特别快,也许这并不重要。但是文档说无论如何大块阅读都更好。 – 2009-05-06 06:46:38

回答

18

recv()调用直接通过调用C库函数来处理。

它会阻止等待套接字有数据。实际上它只会让recv()系统调用阻止。

file.readline()是一种高效的缓冲实现。它不是线程安全的,因为它假设它是唯一一个读取文件。 (例如通过缓存即将到来的输入。)

如果您使用的是文件对象,则每次调用read()时都带有正参数,但底层代码将仅请求数据量recv(),除非它已被缓冲。

这将被缓冲,如果:

  • 你曾打电话的ReadLine(),其内容全缓冲

  • 行的末尾是缓冲区结束前

因此将数据留在缓冲区中。否则,缓冲区通常不会被过度填充。

该问题的目标不明确。如果您在阅读之前需要查看数据是否可用,则可以使用select()或将套接字设置为非阻塞模式,其格式为s.setblocking(False)。然后,如果没有等待数据,读取将返回空白,而不是阻塞。

您是否正在阅读一个文件或多线程套接字?我会让单个工作人员读取套接字并将接收到的项目放入队列以供其他线程处理。

建议咨询Python Socket Module sourceC Source that makes the system calls

22

如果您关心的性能和控制插座完全 (你是不是传递到例如库),然后尝试实现 自己的缓存在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)的比较会很有趣。

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 = []