Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/278.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Python中为套接字IO编写字符串缓冲区的最佳方法是什么?_Python_Sockets_Buffer - Fatal编程技术网

在Python中为套接字IO编写字符串缓冲区的最佳方法是什么?

在Python中为套接字IO编写字符串缓冲区的最佳方法是什么?,python,sockets,buffer,Python,Sockets,Buffer,我有一个运行非阻塞事件循环的线程,其他线程可以将字符串放在缓冲区上,以便事件循环写入套接字 我想将字符串累积到缓冲区中,这样就可以通过一次调用send发送多个小字符串 从概念上讲,缓冲区需要做3件事 从缓冲区的开头获取数据块 将数据添加到缓冲区的末尾 释放缓冲区的前n个字节 我做了几件事 StringIO非常适合将字符串写到末尾,但在将数据写入套接字后无法释放数据 collections.deque字节数。内存效率非常低 array.array易于附加字符串。复制切片以读取/转储数据 我当前

我有一个运行非阻塞事件循环的线程,其他线程可以将字符串放在缓冲区上,以便事件循环写入套接字

我想将字符串累积到缓冲区中,这样就可以通过一次调用
send
发送多个小字符串

从概念上讲,缓冲区需要做3件事

  • 从缓冲区的开头获取数据块
  • 将数据添加到缓冲区的末尾
  • 释放缓冲区的前n个字节
  • 我做了几件事

    • 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)