【问题标题】:Treatment of large size packets at Twisted在 Twisted 处理大尺寸数据包
【发布时间】:2018-06-29 20:15:20
【问题描述】:

我目前正在构建一个 TCP 服务器(该服务器将由我的公司使用,因此它必须处于高生产级别)

我的要求是:

  1. 服务器会很快(它可以同时处理大量请求,因为我们的客户端会定期发送大文件,这会造成瓶颈)。

  2. 服务器将易于维护。

  3. 同时支持尽可能多的用户。

  4. 那个服务器会是TCP服务器,因为它接收我们公司开发的协议消息,需要我自己解析。

在检查了选项之后,我选择了 Python Twisted,因为他似乎满足了第一个要求(而且由于他是在 Python 中,所以第二个要求自己解决了)。

在阅读了 Twisted 的文档后,我想到了一个我还没有找到优雅答案的问题,我的客户向我发送了相当大的数据包,我在这些数据包中做出基于子序列的决策。

假设我有 1000 个全为零的第一个字节,然后是另外 5,000 个全为 0x10 的字节,我将发回“Hello world”,如果我得到 2000 个全为 0x50 的字节,我将回答“大家好”。

我对 Twisted 的问题是数据最终会到达“protocol.Protocol”,并使用“dataReceived(self,data)”函数处理它们,而这一瞬间是一次性瞬间,这意味着如果我第一次只获取一些字节,我将在第二次获取其余字节我不知道如何从第一次保存数据...

我无法将数据保存在协议工厂中,因为我将同时与多个客户端对话,然后一个将使用另一个客户端的数据,出于同样的原因,我不能使用 Globals。

我很确定我不是第一个遇到这个问题的人,我已经看到了几个在线解决方案,其中包括重新实现“protocol.Protocol”,它们确实不够优雅..

有没有简单优雅的方法来解决这个问题? (解决方案必须优雅,因为我在其上添加了多线程,因为我回馈给客户端的内容远不止“Hello World”,而且我不想阻塞服务器)

顺便说一句,如果有经验的人可以为我推荐比 Twisted 更好的解决方案,我会非常高兴。

谢谢

洋子

【问题讨论】:

    标签: python multithreading tcp twisted serve


    【解决方案1】:

    听起来您需要为每个连接状态维护一些。这是您可以存储并仍然使用这种协议的最低数量。您的协议类应该在每个连接中实例化一次,因此您应该能够使用该类的属性来存储 zeros_receivedtwos_received 之类的内容。

    def dataReceived(self, data):
        if self.zeros_received != 1000:
            for x, b in enumerate(data):
                if b != 0x00:
                    # Handle unexpected byte error
                self.zeros_received += 1
    
                if self.zeros_received == 1000:
                    break
    
        if self.twos_received != 5000:
            for b in data[x+1:]:
                if b != 0x10:
                    # handle unexpected byte error
                self.twos_received += 1
    
                if self.twos_received == 5000:
                    break
    
       # send hello...
    

    更简单的解决方案是缓冲来自客户端的数据,然后阻塞(连接超时),直到您收到前 6k 字节。我会小心过早优化。您现在假设这将是您的瓶颈,但通常您的假设可能是错误的。首先实现一个简单的解决方案(使用缓冲读取器),然后对速度/内存使用情况进行基准测试,看看实际需要改进的地方。

    def dataReceived(self, data):
        self.data += data
    
        if len(self.data) >= 6000:
            assert all(lambda b: b == 0x00, self.data[:1000]), 'expected 0x00'
            assert all(lambda b: b == 0x10, self.data[1000:6000]), 'expected 0x10'
    
            # send hello
    

    【讨论】:

    • 感谢您的回复。如果我没有正确解释自己,我深表歉意,我正在寻找维护每个连接状态问题的通用解决方案,而不是针对我带来的示例的特定解决方案(我只是将其作为示例而不是我的协议)。如果我有一个基于前 100K 字节的状态机,我不能使用你建议的解决方案,因为如果我实现它,我将完全阻塞我的服务器并失去同时处理多个客户端的能力。
    • 对此没有通用的解决方案。我试图说明您可以存储协议的状态而不必存储前 6k 字节。我的第一个例子是一个 FSM,它只需要存储 2 个数字就可以处理这样的协议。你不能完全不存储任何状态。但听起来你可能对扭曲的工作原理有误解。 Twisted 使用事件循环。它一次处理多个连接。由于dataReceived 的调用方式,一个连接不会阻止另一个连接,因为没有阻塞读取。
    • “Put stuff on self”正是维护每个连接状态问题的通用解决方案。如果您的协议与问题中描述的不符,那仅意味着您将使用 data 做不同的事情,并将不同的“东西”放在自己身上。
    猜你喜欢
    • 2017-09-21
    • 2023-03-29
    • 2021-12-15
    • 2011-04-18
    • 2013-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    相关资源
    最近更新 更多