使用自定义帧结构处理串联TCP流的Twisted协议?

时间:2015-11-27 02:16:44

标签: python tcp twisted

我得到了一个工作来构建一个长拉式TCP套接字服务器作为设备服务器。在我的开发过程中选择了Twisted。它适用于我的Python设备模拟器。但是,真实设备会发送连接(或组合)的TCP数据包。我知道在真实网络和设备中这是正常的,尽管TCP数据包很短。

它有三种框架结构:

  

\ xFDAA +“realtime_data”+ \ xCCDD(长度固定为150B)

     

\ xFDCC +“extra_data”+ \ xCCDD(长度固定为190B)

     

\ xFDCC +“extra_data”+ \ xCCDD(长度固定为192B)

显然,\ xFDAA \ xFDCC是标题,\ xCCDD是EOT。所以他们确实有债券。它们还暗示固定长度,没有在协议本身中定义。

但是,我不知道如何使用退出的Twisted方法处理自定义帧的连接数据包。在我的开发人员中,我使用了dataReceiver。

到目前为止,我正在尝试解析数据包并将其存储在Factory of protocol中的缓冲区中。当每个新数据包到达时,我会将先前缓冲的数据与新数据组合进行解析(如果连接则组合,如果收到组合数据包则将它们分开......但这看起来很脏)。

我查看了twistedmatrix.com的常见问题解答。它建议采用以下解决方案:

LineReceiver (with \r\n ending chars) 
NetstringReceiver (with callback for every string received) 
Int8/16/32Receiver (with prefix length information)

然后还建议使用AMP和PB高级消息传递。

我希望听到扭曲的专家提出的关于如何正式扭曲实施的任何建议。并且URL /演示代码非常有用。

1 个答案:

答案 0 :(得分:5)

LineReceiverNetstringReceiverInt8/16/32ReceiverAMPPB适用于您的问题,因为它们都是实施 特定框架(以及后两种情况下的消息传递)协议。 相反,您有一个要实现的自定义协议。

幸运的是,这是相对简单的:Twisted通过你的数据提供数据 IProtocol实施' dataReceived方法。

处理此类事情的最佳方法实际上是实现一个简单的方法 首先发挥作用,而不是担心它究竟是如何插入的 变成扭曲的。在您的情况下,您需要一个解析协议的函数; 但是,由于dataReceived可能会为您提供部分数据包,因此您需要 确保函数返回两件事:解析的数据和剩余的数据 缓冲。完成此功能后,您可以将其插入Protocol 子类很容易。

你对协议的解释不是很清楚,所以这可能不太完整 是的,但我将您对消息格式的描述解释为:

octet 0xFD
octet 0xAA
150 octets of "realtimeData"
octet 0xCC
octet 0xDD
octet 0xFD
octet 0xCC
190 octets of "extraData1"
octet 0xCC
octet 0xDD
octet 0xFD
octet 0xCC
192 octets of "extraData2"
octet 0xCC
octet 0xDD
换句话说,单个协议消息长544个字节,包含3个 字段和12个字节的填充必须是正确的。

所以让我们先写一个Message类来表示带有这些的消息 三个字段,使用标准库struct模块进行解析和序列化 它的领域:

from struct import Struct

class Message(object):
    format = Struct(
        "!" # Network endian; always good form.
        "2s" # FD AA
        "150s" # realtimeData
        "4s" # CC DD FD CC
        "190s" # extra1
        "4s" # CC DD FD CC
        "192s" # extra2
        "2s" # CC DD
    )

    def __init__(self, realtimeData, extra1, extra2):
        self.realtimeData = realtimeData
        self.extra1 = extra1
        self.extra2 = extra2

    def toBytes(self):
        return self.format.pack(
            b"\xFD\xAA", self.realtimeData, b"\xCC\xDD\xFD\xCC", self.extra1,
            b"\xCC\xDD\xFD\xCC", self.extra2, b"\xCC\xDD"
        )

    @classmethod
    def fromBytes(cls, octets):
        [fdaa, realtimeData, ccddfdcc, extra1, ccddfdcc2, extra2,
         ccdd] = cls.format.unpack(octets)
        # verify message integrity
        assert fdaa == b"\xFD\xAA"
        assert ccddfdcc == b"\xCC\xDD\xFD\xCC"
        assert ccddfdcc2 == b"\xCC\xDD\xFD\xCC"
        assert ccdd == b"\xCC\xDD"
        return cls(realtimeData, extra1, extra2)

    @classmethod
    def parseStream(cls, streamBytes):
        sz = cls.format.size
        messages = []
        while len(streamBytes) >= sz:
            messageData, streamBytes = streamBytes[:sz], streamBytes[sz:]
            messages.append(cls.fromBytes(messageData))
        return messages, streamBytes

这里与Twisted接口的重要部分是决赛 parseStream方法,它将一堆字节变成一堆消息,并且 尚未解析的其余字节。然后我们可以Protocol 理解一个真实的网络流,如下所示:

from twisted.internet.protocol import Protocol
class MyProtocol(Protocol):
    buffer = b""
    def dataReceived(self, data):
        messages, self.buffer = Message.parseStream(self.buffer + data)
        for message in messages:
            self.messageReceived(message)
    def messageReceived(self, message):
        "do something useful with a message"

您可能想要调用方法,而不是调用self.messageReceived self的其他一些属性,或者可能将Message对象传递给self.factory.messagesBuffer.append(message) 与此协议相关的工厂。它取决于你!既然你说过 你想要解析数据包并将其存储在Factory"的缓冲区中,也许是你 只想做rails_12factor。希望这个 看起来比你的"数据包连接更清晰"方法,但不是 我已经清楚地描述了你认为令人反感的东西 关于它。