代码之家  ›  专栏  ›  技术社区  ›  Jack Edmonds

如何使用python和google的协议缓冲区来反序列化通过TCP发送的数据

  •  17
  • Jack Edmonds  · 技术社区  · 15 年前

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

    在python中有什么好的实践可以做到这一点吗?

    3 回复  |  直到 11 年前
        1
  •  36
  •   J.J.    15 年前

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

    发送端大致是:

    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)
    

    您最终得到的是一个类似于以下格式的over-the-wire消息格式:

    struct {
         unsigned char type;
         unsigned short length;
         void *data;
    }
    

    这对将来防止不可预见的需求的有线协议做了合理的工作。这是一个 Type-Length-Value 协议,您将在网络协议中一次又一次地找到它。

        2
  •  4
  •   frymaster    15 年前

    为了扩展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对象。

    (*)我认为可以通过在容器消息中封装特定消息来绕过这一点。

        3
  •  0
  •   davidA    11 年前

    要考虑的另一个方面(尽管是在更简单的情况下)是您对单个消息使用单个TCP连接。在这种情况下,只要您知道期望的消息是什么(或使用 Union Types 要在运行时确定消息类型,可以使用tcp connection open作为“start”分隔符,使用connection close事件作为最终分隔符。这样做的好处是,您可以快速接收整个消息(而在其他情况下,TCP流可以保持一段时间,从而延迟接收整个消息)。如果这样做,就不需要任何显式带内帧,因为TCP连接的生存期充当帧本身。