题
假设我想使用标准从套接字读取一行 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)
?每次都会发出系统调用吗?无论如何,我想我应该添加一些缓冲:
为了与硬件和网络现实最佳匹配, 缓冲区大小 应该是一个相对较小的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()
解决方案
这 recv()
调用是直接通过调用C库函数来处理的。
它将阻塞等待套接字有数据。实际上它只会让 recv()
系统调用块。
file.readline()
是一种高效的缓冲实现。它不是线程安全的,因为它假定它是唯一读取该文件的人。(例如通过缓冲即将到来的输入。)
如果您使用文件对象,则每次 read()
用正参数调用,底层代码将 recv()
仅请求的数据量,除非已缓冲。
如果出现以下情况,它将被缓冲:
你调用了 readline(),它读取一个完整的缓冲区
行尾在缓冲区末尾之前
从而将数据留在缓冲区中。否则缓冲区通常不会被填满。
提问的目的不明确。如果您需要在阅读之前查看数据是否可用,您可以 select()
或将套接字设置为非阻塞模式 s.setblocking(False)
. 。然后,如果没有等待数据,则读取将返回空,而不是阻塞。
您正在使用多个线程读取一个文件或套接字吗?我会让一个工作人员读取套接字并将接收到的项目送入队列以供其他线程处理。
建议咨询 Python Socket 模块源码 和 进行系统调用的 C 源.
其他提示
如果您关心性能并完全控制插座 (例如,你没有将它传递到库中)然后尝试实现 你自己在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)进行比较。
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 = []