在Python中为套接字IO编写字符串缓冲区的最佳方法是什么?
我有一个运行非阻塞事件循环的线程,其他线程可以将字符串放在缓冲区上,以便事件循环写入套接字 我想将字符串累积到缓冲区中,这样就可以通过一次调用在Python中为套接字IO编写字符串缓冲区的最佳方法是什么?,python,sockets,buffer,Python,Sockets,Buffer,我有一个运行非阻塞事件循环的线程,其他线程可以将字符串放在缓冲区上,以便事件循环写入套接字 我想将字符串累积到缓冲区中,这样就可以通过一次调用send发送多个小字符串 从概念上讲,缓冲区需要做3件事 从缓冲区的开头获取数据块 将数据添加到缓冲区的末尾 释放缓冲区的前n个字节 我做了几件事 StringIO非常适合将字符串写到末尾,但在将数据写入套接字后无法释放数据 collections.deque字节数。内存效率非常低 array.array易于附加字符串。复制切片以读取/转储数据 我当前
send
发送多个小字符串
从概念上讲,缓冲区需要做3件事
非常适合将字符串写到末尾,但在将数据写入套接字后无法释放数据StringIO
字节数。内存效率非常低collections.deque
易于附加字符串。复制切片以读取/转储数据array.array
from array import array
from threading import Condition
class SendBuffer(object):
def __init__(self, max_size):
self.mark = 0
self.buf = array('c')
self.max_size = max_size
self.full = Condition()
def __len__(self):
with self.full:
return len(self.buf) - self.mark
def write(self, data):
with self.full:
while len(self) >= self.max_size:
# wait until data is written
self.full.wait()
self.buf.fromstring(data)
def _peek(self):
return buffer(self.buf, self.mark)
def _written(self, n):
self.mark += n
self.full.notify_all()
if self.mark >= len(self.buf):
self.mark = 0
self.buf = array('c')
elif self.mark >= self.max_size:
self.buf = self.buf[self.mark:]
self.mark = 0
def to_sock(self, sock):
with self.full:
data = self._peek()
if data:
n = sock.send(data)
self._written(n)
您的问题是,您的缓冲区(如StringIO)只能有效地附加到。完成处理后,请执行以下操作,而不是追加到末尾并从前面删除:
- 找两个缓冲区李>
- 在任何时刻,所有写操作都将转到其中一个(结尾),读取器将从另一个读取(如果缓冲区大于所需大小,则使用索引保持位置,可以在一次send()调用中写入)
- 当读卡器读取完一个缓冲区后,缓冲区被清除(*),它们交换角色
- 读卡器的速度快于写卡器:每次写入后都会紧接着进行相同大小的读取,并且缓冲区处于交换位置。每次写入都会立即作为一个数据包发出
- 读卡器和写卡器完全同步,或者非常接近,但有些抖动:在读卡器完成之前,多个小的写操作会累积到写缓冲区中,然后以网络所能承受的大小的块发送出去
- 作者比读者快。读卡器忙于处理读缓冲区时,写缓冲区将填满。读卡器发送的数据块仍然与网络接收的数据块一样大,但您需要以某种方式限制写卡器(通常通过设置最大缓冲区大小)并对其进行调节,以避免占用无限的内存
(*)清除一个StringIO对象显然不是一件微不足道的事情,谷歌搜索一下就知道了。您可能希望创建一个新对象而不是清除,但这可能会导致大量垃圾,如果您有大量的上下文开关,则需要对这些垃圾进行GC。相反,您还可以考虑使用数组和<代码>索引< /代码>变量的组合来构建自己的可清除缓冲区,在这种情况下,清除将归结为<代码>索引=0 。双重缓冲!清除StringIO与查找(0)一样简单。为了限制缓冲区的大小,您必须始终跟踪缓冲区的长度,以便您可以使用该长度仅读取当前数据。假设我的缓冲区有100个字节长,我向套接字写入了99个字节,现在我有了一个可能已满的backbuffer,还有一个frontbuffer,下次套接字准备就绪时,它将写入一个字节。这是一个很好的观点。另一方面,由于我们有一个动态缓冲区,唯一的目标是减少无限的缓冲区增长(而不是将内存使用限制在精确的字节数),因此您可以有一个简单的规则,如“允许写入,如果len(buffer)