如何使用Python和Google';s协议缓冲区,用于反序列化通过TCP发送的数据

如何使用Python和Google';s协议缓冲区,用于反序列化通过TCP发送的数据,python,tcp,protocol-buffers,Python,Tcp,Protocol Buffers,我正在尝试编写一个应用程序,它使用Google的协议缓冲区通过TCP连接反序列化数据(使用协议缓冲区从另一个应用程序发送)。问题在于Python中的协议缓冲区似乎只能从字符串中反序列化数据。由于TCP没有定义良好的消息边界,并且我尝试接收的消息之一有一个重复字段,因此在最终传递要反序列化的字符串之前,我不知道要尝试接收多少数据 在Python中有没有这样做的好方法?不要只将序列化数据写入套接字。首先发送一个包含序列化对象长度的固定大小字段 发送端大致为: socket.write(struct.

我正在尝试编写一个应用程序,它使用Google的协议缓冲区通过TCP连接反序列化数据(使用协议缓冲区从另一个应用程序发送)。问题在于Python中的协议缓冲区似乎只能从字符串中反序列化数据。由于TCP没有定义良好的消息边界,并且我尝试接收的消息之一有一个重复字段,因此在最终传递要反序列化的字符串之前,我不知道要尝试接收多少数据


在Python中有没有这样做的好方法?

不要只将序列化数据写入套接字。首先发送一个包含序列化对象长度的固定大小字段

发送端大致为:

socket.write(struct.pack("H", len(data))    #send a two-byte size field
socket.write(data)
接收端会变成这样:

dataToRead = struct.unpack("H", socket.read(2))[0]    
data = socket.read(dataToRead)
type = socket.read(1)                                 # get the type of msg
dataToRead = struct.unpack("H", socket.read(2))[0]    # get the len of the msg
data = socket.read(dataToRead)                        # read the msg

if TYPE_FOO == type:
    handleFoo(data)

elif TYPE_BAR == type:
    handleBar(data)

else:
    raise UnknownTypeException(type)
这是套接字编程的常见设计模式。大多数设计都将线结构扩展到包括类型字段,因此接收端类似于:

dataToRead = struct.unpack("H", socket.read(2))[0]    
data = socket.read(dataToRead)
type = socket.read(1)                                 # get the type of msg
dataToRead = struct.unpack("H", socket.read(2))[0]    # get the len of the msg
data = socket.read(dataToRead)                        # read the msg

if TYPE_FOO == type:
    handleFoo(data)

elif TYPE_BAR == type:
    handleBar(data)

else:
    raise UnknownTypeException(type)
您最终得到的是一种无线消息格式,如下所示:

struct {
     unsigned char type;
     unsigned short length;
     void *data;
}
这是一项合理的工作,可以防止有线协议遇到不可预见的需求。这是一个协议,你会在网络协议中一次又一次地找到它

要扩展J.J.(完全正确)的答案,protobuf库无法计算消息本身的长度,也无法计算发送的protobuf对象的类型*。因此,向您发送数据的另一个应用程序必须已经在执行类似的操作

当我不得不这样做时,我实现了一个查找表:

messageLookup={0:foobar_pb2.MessageFoo,1:foobar_pb2.MessageBar,2:foobar_pb2.MessageBaz}
…基本上做了J.J.做的事,但我也有一个助手功能:

    def parseMessage(self,msgType,stringMessage):
        msgClass=messageLookup[msgType]
        message=msgClass()
        message.ParseFromString(stringMessage)
        return message
…我调用它将字符串转换为protobuf对象


(*)我认为可以通过封装容器消息中的特定消息来解决这一问题:

< P>另一个要考虑的方面(尽管更简单的情况)是在单个消息中使用单个TCP连接。在这种情况下,只要您知道预期的消息是什么(或用于在运行时确定消息类型),就可以使用TCP连接打开作为“开始”分隔符,使用连接关闭事件作为最终分隔符。这样做的好处是您可以快速接收整个消息(而在其他情况下,TCP流可以保留一段时间,从而延迟整个消息的接收)。如果您这样做,您就不需要任何显式的带内帧,因为TCP连接的生命周期本身就是一个帧。

+1提供了一个非常详细和令人敬畏的答案。非常感谢。使用
struct.pack(“H”,len(data))
会导致一个重要结果:数据长度必须小于65536字节。您可以通过使用无符号long-long而不是
Q
(最大大小=18000 PB)来增加数据的最大允许大小。这两个答案都是好的,但(根据我的说法)封装是前进的方向。