首页 >后端开发 >Golang >如何在没有类型信息的情况下解析未知的 Protobuf 消息?

如何在没有类型信息的情况下解析未知的 Protobuf 消息?

Patricia Arquette
Patricia Arquette原创
2024-11-28 01:36:10620浏览

How Can I Parse Unknown Protobuf Messages Without Type Information?

没有类型信息的未知 Proto 消息解组

虽然 protobuf 的 proto.Unmarshal() 方法需要已知的消息类型,但未知消息提出了挑战。但是,我们可以使用 protowire 包提取有限的信息。

方法:

  1. 使用 protowire.ConsumeField() 迭代未知的消息负载。
  2. 标识字段类型,可以为:

    • Varint
    • Fixed64
    • 字节
    • StartGroup
    • Fixed32
  3. 根据字段payload解析类型。
  4. 通过递归解析子消息的有效负载来处理子消息。

数据模型:

type Field struct {
    Tag Tag
    Val Val
}

type Tag struct {
    Num int32
    Type protowire.Type
}

type Val struct {
    Payload interface{}
    Length int
}

解析器:

func parseUnknown(b []byte) []Field {
    // Iteratively consume and parse fields
    for len(b) > 0 {
        // Read field tag and length
        n, t, fieldlen := protowire.ConsumeField(b)
        if fieldlen < 1 {
            return nil
        }
        field := Field{Tag: Tag{Num: int32(n), Type: t}}

        // Read and process tag and value content
        _, _, taglen := protowire.ConsumeTag(b[:fieldlen])
        if taglen < 1 {
            return nil
        }
        var v interface{}
        var vlen int
        switch t {
            case protowire.VarintType:
                v, vlen = protowire.ConsumeVarint(b[taglen:fieldlen])
            case protowire.Fixed64Type:
                v, vlen = protowire.ConsumeFixed64(b[taglen:fieldlen])
            case protowire.BytesType:
                v, vlen = protowire.ConsumeBytes(b[taglen:fieldlen])
                sub := parseUnknown(v.([]byte))
                if sub != nil {
                    v = sub
                }
            case protowire.StartGroupType:
                v, vlen = protowire.ConsumeGroup(n, b[taglen:fieldlen])
                sub := parseUnknown(v.([]byte))
                if sub != nil {
                    v = sub
                }
            case protowire.Fixed32Type:
                v, vlen = protowire.ConsumeFixed32(b[taglen:fieldlen])
        }
        if vlen < 1 {
            return nil
        }

        field.Val = Val{Payload: v, Length: vlen - taglen}
        fields = append(fields, field)
        b = b[fieldlen:]
    }
    return fields
}

示例(消息结构):

message Foo {
  string a = 1;
  string b = 2;
  Bar bar = 3;
}

message Bar {
  string c = 1;
}

示例(已解析输出):

main.Field{Tag:main.Tag{Num:1, Type:2}, Val:main.Val{Payload:[]uint8{0x41}, Length:1}}
main.Field{Tag:main.Tag{Num:2, Type:2}, Val:main.Val{Payload:[]uint8{0x42}, Length:1}}
main.Field{Tag:main.Tag{Num:1, Type:2}, Val:main.Val{Payload:[]uint8{0x43}, Length:1}}
main.Field{Tag:main.Tag{Num:3, Type:2}, Val:main.Val{Payload:[]main.Field{main.Field{Tag:main.Tag{Num:1, Type:2}, Val:main.Val{Payload:[]uint8{0x43}, Length:1}}}, Length:3}}

注意事项:

  • 子消息: 递归解析标识为消息的字节有效负载.
  • 重复fields: 根据相同的字段标签编号进行处理。
  • Maps: 假设类似于重复的 k/v 对。
  • Oneofs: Union类型信息丢失,只有实际值

虽然此代码没有提供完整的生产级解决方案,但它提供了一种解析未知原始有效负载的方法,同时保留一些语义信息。

以上是如何在没有类型信息的情况下解析未知的 Protobuf 消息?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn