You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go客户端解析NATS传来的Protobuf报invalid wire-format data问题求助

解决Go客户端反序列化Protobuf数据报错proto: cannot parse invalid wire-format data

错误原因分析

  1. 消息前缀未处理:服务器端发送的字节流包含额外前缀,并非纯Protobuf数据。
  2. 类型映射不匹配:C#端使用的decimal、Guid类型与Go proto定义的string类型序列化格式不兼容。
  3. 潜在枚举不一致:C#与proto文件中的枚举数值可能存在错位。

具体修复步骤

1. 处理消息前缀

服务器端的序列化逻辑先写入1字节的FeedMessageType,再写入带Fixed32长度前缀的Protobuf数据。Go端需先跳过前缀,再提取有效Protobuf数据:

import "encoding/binary"

_, err = sc.Subscribe("EXEC", func(m *stan.Msg) {
    if len(m.Data) < 5 { // 1字节类型标识 + 4字节Fixed32长度
        fmt.Println("Invalid message length")
        return
    }
    // 跳过第一个字节的FeedMessageType
    dataAfterType := m.Data[1:]
    // 解析Fixed32格式的长度前缀(大端字节序)
    payloadLength := binary.BigEndian.Uint32(dataAfterType[:4])
    // 提取实际的Protobuf payload
    protoPayload := dataAfterType[4 : 4+payloadLength]

    varr := &protos.Execution{}
    err = proto.Unmarshal(protoPayload, varr)
    if err != nil {
        fmt.Printf("Err unmarshalling!: %v\n\n", err.Error())
    } else {
        fmt.Printf("Received a message: %+v\n", varr)
    }
})

2. 修正类型映射

C#的decimal和Guid类型与官方Protobuf的string序列化格式不兼容,需调整proto定义:

修改.proto文件中的字段类型

// TradeData调整
message TradeData {
  int64 TradeId = 1;
  bytes Amount = 4;       // 对应C# decimal(二进制格式)
  bytes ExecutionPrice = 5;// 对应C# decimal
  OrderStatus OrderStatus = 7;
  int64 AccountId = 11;
  bytes MatchedOrderExternalId = 14; // 对应C# Guid(16字节二进制)
  int64 MatchedOrderId = 16;
  bytes RemainingAmount = 17; // 对应C# decimal
}

// Execution调整
message Execution {
  string Origin = 4;
  OrderSide Side = 7;
  bytes RequestedPrice = 8;    // 对应C# decimal
  bytes RequestedAmount = 9;   // 对应C# decimal
  bytes RemainingAmount = 10;  // 对应C# decimal
  int64 ExecutedAt = 13;
  OrderStatus OrderStatus = 14;
  repeated TradeData Trades = 16;
  OrderType OrderType = 20;
  int64 Version = 22;
  int64 AccountId = 23;
  RejectReason RejectReason = 25;
  ReportCause ReportCause = 26;
  bytes InstructionId = 27;      // 对应C# Guid
  bytes ExternalOrderId = 28;     // 对应C# Guid
  int32 ExecutionEngineMarketId = 29;
  int64 OrderId = 30;
  CancelReason CancelReason = 31;
  int64 TxId = 32;
  TimeInForce TimeInForce = 34;
  string CancelledBy = 35;
}

Go端解析二进制数据

  • 对于Guid(16字节):使用第三方库将bytes转为UUID:
    import "github.com/google/uuid"
    // 解析MatchedOrderExternalId示例
    uuidVal, err := uuid.FromBytes(varr.Trades[0].MatchedOrderExternalId)
    
  • 对于decimal:使用第三方库解析protobuf-net的decimal二进制格式(protobuf-net将decimal序列化为4个int32:flags, lo, mid, hi):
    import (
      "fmt"
      "math/big"
      "encoding/binary"
      "github.com/shopspring/decimal"
    )
    
    func bytesToDecimal(b []byte) (decimal.Decimal, error) {
        if len(b) != 16 {
            return decimal.Zero, fmt.Errorf("invalid decimal bytes length")
        }
        flags := binary.LittleEndian.Uint32(b[:4])
        lo := binary.LittleEndian.Uint32(b[4:8])
        mid := binary.LittleEndian.Uint32(b[8:12])
        hi := binary.LittleEndian.Uint32(b[12:16])
        return decimal.FromBigInt(new(big.Int).SetUint64(uint64(hi)<<32|uint64(mid)), int32(flags>>16)&0xff), nil
    }
    

3. 验证枚举一致性

检查C#与proto文件中所有枚举的数值是否完全一致,例如:

  • proto中OrderStatus.Working = 0,C#中OrderStatus.Working的数值也必须为0
  • 所有枚举项的数值一一对应,无遗漏或错位

4. 重新生成Go代码并测试

修改proto文件后,重新生成Go的Protobuf代码:

protoc --go_out=. execution.proto

然后运行Go客户端测试反序列化是否正常。


内容的提问来源于stack exchange,提问作者Divyalok Jaiswal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 22:14:56