如何使用Python和Google的Protocol Buffers来反序列化通过TCP发送的数据

时间:2010-01-10 18:45:29

标签: python tcp protocol-buffers

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

在Python中执行此操作有什么好的做法吗?

3 个答案:

答案 0 :(得分:36)

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

发送方大致是:

socket.write(struct.pack("H", len(data))    #send a two-byte size field
socket.write(data)

并且recv'ing方面变得像:

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;
}

这可以合理地解决线路协议未来不可预见的要求。这是一个Type-Length-Value协议,您可以在网络协议中一次又一次地找到它。

答案 1 :(得分:4)

为了扩展JJ(完全正确)的答案,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对象。

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

答案 2 :(得分:0)

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