All*_*Liu 1 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。
到目前为止,我试图解析数据包并将其存储在协议工厂的缓冲区中。当每个新数据包到达时,我将以前的缓冲数据与新数据包组合起来进行解析(如果是合并的,则合并,如果接收到合并的数据包,则将它们分开……但这看起来很脏)。
我已经检查了twistedmatrix.com的常见问题。它建议以下解决方案:
LineReceiver (with \r\n ending chars)
NetstringReceiver (with callback for every string received)
Int8/16/32Receiver (with prefix length information)
Run Code Online (Sandbox Code Playgroud)
然后还建议AMP和PB高级消息传递。
我想听听扭曲专家提出的关于如何在扭曲中正式实施它的任何建议。URL /演示代码非常有帮助。
该组的无LineReceiver,NetstringReceiver,Int8/16/32Receiver,
AMP,或PB适用于您的问题,因为它们是特定帧的所有实现(在后者的两个消息的情况下)的协议。相反,您具有要实现的自定义协议。
幸运的是,这相对简单:Twisted通过IProtocol实现的dataReceived方法为您提供数据
。
处理此类问题的最佳方法实际上是首先实现一个简单的功能,而不用担心确切地将其插入Twisted中的方式。在您的情况下,您需要一个解析协议的函数。但是,由于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
Run Code Online (Sandbox Code Playgroud)
换句话说,单个协议消息的长度为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
Run Code Online (Sandbox Code Playgroud)
这里与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"
Run Code Online (Sandbox Code Playgroud)
与其调用self.messageReceived,不如调用的其他属性的方法self,或者将Message对象中继到与此协议相关联的工厂。由你决定!因为您说过要“解析数据包并将其存储在Factory的缓冲区中”,所以也许您只想这样做self.factory.messagesBuffer.append(message)。希望这看起来比您的“数据包连接”方法更干净,因为对它的描述不够清楚,无法让我理解您的想法。