目录
正确答案
首页 后端开发 Golang proto:无法解析无效的有线格式数据

proto:无法解析无效的有线格式数据

Feb 06, 2024 am 10:57 AM

proto:无法解析无效的有线格式数据

问题内容

我是 protobufs 的新手,目前正在编写一个从 nats 服务器读取数据的客户端。从 nats 服务器发送的数据是 protobuf。

我正在编写的客户端是用 go 编写的。这是我编写的 .proto 文件:

syntax = "proto3";

package execution;

option go_package = "./protos/execution";

enum orderstatus {
  working = 0;
  rejected = 1;
  cancelled = 2;
  completed = 3;
}

enum ordertype {
  limit = 0;
  market = 1;
  stoplimit = 2;
  stopmarket = 3;
}

enum orderside {
  buy = 0;
  sell = 1;
}

enum rejectreason {
  norejection = 0;
  instrumentnotfound = 1;
  ordernotfound = 2;
  invalidordertype = 3;
  invalidaccount = 4;
  invalidside = 5;
  invalidamount = 6;
  invalidlimitprice = 7;
  invalidquotelimit = 8;
  invalidactivationprice = 9;
  invalidtimeinforce = 10;
  markethalted = 11;
  marketpaused = 12;
  nocounterorders = 13;
  missingexpirationtime = 14;
  incorrectexpirationtime = 15;
  internalerror = 16;
  illegalstatusswitch = 17;
  orderalreadyexists = 18;
  instrumentnotready = 19;
  externalsystemerror = 20;
}

enum reportcause {
  none = 0;
  neworder = 1;
  cancelorder = 2;
  masscancel = 3;
  expiration = 4;
  trigger = 5;
  marketstatuschange = 6;
}

enum timeinforce {
  goodtillcancel = 0;
  immediateorcancel = 1;
  fillorkill = 2;
}

enum cancelreason {
  notcancelled = 0;
  cancelledbytrader = 1;
  cancelledbysystem = 2;
  selfmatchprevention = 3;
  ordertimeinforce = 4;
  liquidation = 100;
}


message tradedata {
  int64 tradeid = 1;
  string amount = 4;
  string executionprice = 5;
  orderstatus orderstatus = 7;
  int64 accountid = 11;
  string matchedorderexternalid = 14;
  int64 matchedorderid = 16;
  string remainingamount = 17;
}

message execution {
  string origin = 4;
  orderside side = 7;
  string requestedprice = 8;
  string requestedamount = 9;
  string remainingamount = 10;
  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;
  string instructionid = 27;
  string externalorderid = 28;
  int32 executionenginemarketid = 29;
  int64 orderid = 30;
  cancelreason cancelreason = 31;
  int64 txid = 32;
  timeinforce timeinforce = 34;
  string cancelledby = 35;
}
登录后复制

发布服务器是用 c# 编写的,其原始消息的代码如下:

[protocontract]
    public class executionreport : imarketresponse, iinstructionmessage, iordermatcherresponse
    {
        [protoignore]
        feedmessagetype ifeedmessage.type => feedmessagetype.executionreport;

        // resharper disable fieldcanbemadereadonly.global
        [protomember(4)] public string origin;
        [protomember(7)] public orderside side;
        [protomember(8)] public decimal requestedprice;
        [protomember(9)] public decimal requestedamount;
        [protomember(10)] public decimal remainingamount;
        [protomember(13)] public long executedat;
        [protomember(14)] public orderstatus orderstatus;
        [protomember(16)] public list<tradedata> trades = new list<tradedata>();
        [protomember(20)] public ordertype ordertype;
        [protomember(22)] public long version { get; set; }
        [protomember(23)] public long accountid;
        [protomember(25)] public rejectreason rejectreason;
        [protomember(26)] public reportcause reportcause;
        [protomember(27)] public guid instructionid { get; set; }
        [protomember(28)] public guid externalorderid;
        [protomember(29)] public int executionenginemarketid { get; set; }
        [protomember(30)] public long orderid;
        [protomember(31)] public cancelreason cancelreason;
        [protomember(32)] public long txid;
        [protomember(34)] public timeinforce timeinforce;
        [protomember(35)] public string cancelledby;
    }

[protocontract]
    [structlayout(layoutkind.sequential)]
    public struct tradedata
    {
        [protomember(1)] public long tradeid;
        [protomember(4)] public decimal amount;
        [protomember(5)] public decimal executionprice;
        [protomember(7)] public orderstatus orderstatus;
        [protomember(11)] public long accountid;
        [protomember(14)] public guid matchedorderexternalid;
        [protomember(16)] public long matchedorderid;
        [protomember(17)] public decimal remainingamount;
    }
登录后复制

在尝试解组数据时出现此错误

proto: cannot parse invalid wire-format data
登录后复制

这就是我解析数据的方式:

_, err = sc.subscribe("exec", func(m *stan.msg) {
varr := &protos.execution{}
err = proto.unmarshal(m.data, varr)
if err != nil {
    fmt.printf("err unmarshalling!: %v\n\n", err.error())
} else {
    fmt.printf("received a message: %+v\n", varr)
}
登录后复制

我从服务器接收到的示例字节数据:

[5 85 0 0 0 56 1 66 3 8 144 78 74 2 8 1 82 2 8 1 104 197 192 132 194 159 143 219 237 8 176 1 25 184 1 11 208 1 1 218 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 226 1 18 9 133 66 138 247 239 67 93 77 17 176 192 189 75 170 203 186 145 232 1 1 240 1 25 128 2 25]
登录后复制

添加更多详细信息:

这就是 c# 发送数据的方式:

public async task sendasync(ifeedmessage msg)
{
    var subject = feedsubject.formessage(msg);
    var data = msg.serializetoarray();
    using (_metrics.feedsendlatency.start(new metrictags("subject", subject.value)))
    {
        await _connection.publishasync(subject, data);
    }
}
登录后复制

这是feedmessage的结构(executionreport也间接继承它)

public interface ifeedmessage
{
    feedmessagetype type { get; }
    ifeedmessage clone();
    void reset();
}
登录后复制

这就是 serializetoarray() 的工作原理:

public static ArraySegment<byte> SerializeToArray(this IFeedMessage message)
{
    return message.SerializeToMemory(new MemoryStream());
}

public static ArraySegment<byte> SerializeToMemory(this IFeedMessage message, MemoryStream stream)
{
    var start = stream.Position;
    message.Serialize(stream);
    return new ArraySegment<byte>(stream.GetBuffer(), (int)start, (int)(stream.Position - start));
}

public static void Serialize(this IFeedMessage message, Stream stream)
{
    stream.WriteByte((byte)message.Type);
    RuntimeTypeModel.Default.SerializeWithLengthPrefix(stream, message, message.GetType(), PrefixStyle.Fixed32, 0);
}
登录后复制

不知道具体原因是什么。但我写的proto文件似乎是错误的。我浏览了几篇面临相同错误的帖子,但大多数都没有解决相同的问题。如果需要任何其他详细信息,请告诉我。

请帮我解决这个问题。


正确答案


根据评论中的讨论,我成功地整理了数据。

注释

  1. 数据以 5 个字节为前缀(这是完全没有必要的):
    • 消息类型 1 个字节
    • 数据长度为 4 个字节
  2. c# 实现使用特定于 c# 的 decimalguid 数据类型。 (正如 bcl.proto 中所评论的,跨平台代码通常应该完全避免它们)。

这是文件夹结构:

├── bcl.proto
├── execution.proto
├── go.mod
├── go.sum
├── main.go
└── protos
    ├── bcl.pb.go
    └── execution.pb.go
登录后复制

bcl.proto

此文件是从 github 复制的。 com/protobuf-net/protobuf-net。这是必需的,因为 .net 实现使用此原始文件中的 decimalguid

// the types in here indicate how protobuf-net represents certain types when using protobuf-net specific
// library features. note that it is not *required* to use any of these types, and cross-platform code
// should usually avoid them completely (ideally starting from a .proto schema)

// some of these are ugly, sorry. the timespan / datetime dates here pre-date the introduction of timestamp
// and duration, and the "well known" types should be preferred when possible. guids are particularly
// awkward - it turns out that there are multiple guid representations, and i accidentally used one that
// i can only call... "crazy-endian". just make sure you check the order!

// it should not be necessary to use bcl.proto from code that uses protobuf-net

syntax = "proto3";

option csharp_namespace = "protobuf.bcl";
option go_package = "./protos";

package bcl;

message timespan {
  sint64 value = 1; // the size of the timespan (in units of the selected scale)
  timespanscale scale = 2; // the scale of the timespan [default = days]
  enum timespanscale {
    days = 0;
    hours = 1;
    minutes = 2;
    seconds = 3;
    milliseconds = 4;
    ticks = 5;

    minmax = 15; // dubious
  }
}

message datetime {
  sint64 value = 1; // the offset (in units of the selected scale) from 1970/01/01
  timespanscale scale = 2; // the scale of the timespan [default = days]
  datetimekind kind = 3; // the kind of date/time being represented [default = unspecified]
  enum timespanscale {
    days = 0;
    hours = 1;
    minutes = 2;
    seconds = 3;
    milliseconds = 4;
    ticks = 5;

    minmax = 15; // dubious
  }
  enum datetimekind
  {
     // the time represented is not specified as either local time or coordinated universal time (utc).
     unspecified = 0;
     // the time represented is utc.
     utc = 1;
     // the time represented is local time.
     local = 2;
   }
}

message netobjectproxy {
  int32 existingobjectkey = 1; // for a tracked object, the key of the **first** time this object was seen
  int32 newobjectkey = 2; // for a tracked object, a **new** key, the first time this object is seen
  int32 existingtypekey = 3; // for dynamic typing, the key of the **first** time this type was seen
  int32 newtypekey = 4; // for dynamic typing, a **new** key, the first time this type is seen
  string typename = 8; // for dynamic typing, the name of the type (only present along with newtypekey)
  bytes payload = 10; // the new string/value (only present along with newobjectkey)
}

message guid {
  fixed64 lo = 1; // the first 8 bytes of the guid (note:crazy-endian)
  fixed64 hi = 2; // the second 8 bytes of the guid (note:crazy-endian)
}

message decimal {
  uint64 lo = 1; // the first 64 bits of the underlying value
  uint32 hi = 2; // the last 32 bis of the underlying value
  uint32 signscale = 3; // the number of decimal digits (bits 1-16), and the sign (bit 0)
}
登录后复制

execution.proto

syntax = "proto3";

package execution;

option go_package = "./protos";

import "bcl.proto";

enum orderstatus {
  working = 0;
  rejected = 1;
  cancelled = 2;
  completed = 3;
}

enum ordertype {
  limit = 0;
  market = 1;
  stoplimit = 2;
  stopmarket = 3;
}

enum orderside {
  buy = 0;
  sell = 1;
}

enum rejectreason {
  norejection = 0;
  instrumentnotfound = 1;
  ordernotfound = 2;
  invalidordertype = 3;
  invalidaccount = 4;
  invalidside = 5;
  invalidamount = 6;
  invalidlimitprice = 7;
  invalidquotelimit = 8;
  invalidactivationprice = 9;
  invalidtimeinforce = 10;
  markethalted = 11;
  marketpaused = 12;
  nocounterorders = 13;
  missingexpirationtime = 14;
  incorrectexpirationtime = 15;
  internalerror = 16;
  illegalstatusswitch = 17;
  orderalreadyexists = 18;
  instrumentnotready = 19;
  externalsystemerror = 20;
}

enum reportcause {
  none = 0;
  neworder = 1;
  cancelorder = 2;
  masscancel = 3;
  expiration = 4;
  trigger = 5;
  marketstatuschange = 6;
}

enum timeinforce {
  goodtillcancel = 0;
  immediateorcancel = 1;
  fillorkill = 2;
}

enum cancelreason {
  notcancelled = 0;
  cancelledbytrader = 1;
  cancelledbysystem = 2;
  selfmatchprevention = 3;
  ordertimeinforce = 4;
  liquidation = 100;
}


message tradedata {
  int64 tradeid = 1;
  bcl.decimal amount = 4;
  bcl.decimal executionprice = 5;
  orderstatus orderstatus = 7;
  int64 accountid = 11;
  bcl.guid matchedorderexternalid = 14;
  int64 matchedorderid = 16;
  bcl.decimal remainingamount = 17;
}

message execution {
  bytes origin = 4;
  orderside side = 7;
  bcl.decimal requestedprice = 8;
  bcl.decimal requestedamount = 9;
  bcl.decimal remainingamount = 10;
  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;
  bcl.guid instructionid = 27;
  bcl.guid externalorderid = 28;
  int32 executionenginemarketid = 29;
  int64 orderid = 30;
  cancelreason cancelreason = 31;
  int64 txid = 32;
  timeinforce timeinforce = 34;
  string cancelledby = 35;
}
登录后复制

原型/

此文件夹中的文件是使用以下命令从 proto 文件生成的:

protoc --go_out=protos --go_opt=paths=source_relative bcl.proto execution.proto
登录后复制

go.mod

module mymodule.local

go 1.20

require google.golang.org/protobuf v1.30.0
登录后复制

ma​​in.go

package main

import (
    "encoding/binary"
    "log"

    "google.golang.org/protobuf/proto"

    "mymodule.local/protos"
)

func main() {
    data := []byte{5, 85, 0, 0, 0, 56, 1, 66, 3, 8, 144, 78, 74, 2, 8, 1, 82, 2, 8, 1, 104, 197, 192, 132, 194, 159, 143, 219, 237, 8, 176, 1, 25, 184, 1, 11, 208, 1, 1, 218, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 226, 1, 18, 9, 133, 66, 138, 247, 239, 67, 93, 77, 17, 176, 192, 189, 75, 170, 203, 186, 145, 232, 1, 1, 240, 1, 25, 128, 2, 25}
    if len(data) < 5 {
        log.fatal("data should contain at least 5 bytes")
    }
    messagetype := data[0]
    length := binary.littleendian.uint32(data[1:5])
    data = data[5:]
    if length != uint32(len(data)) {
        log.fatalf("invalid data length: %d", length)
    }
    execution := &protos.execution{}

    err := proto.unmarshal(data, execution)
    if err != nil {
        log.fatalf("err unmarshalling!: %v", err)
    }

    log.printf("message type: %d, message: %+v", messagetype, execution)
}
登录后复制

问题中提供的数据的输出:

2023/06/15 17:50:58 message type: 5, message: Side:Sell  RequestedPrice:{lo:10000}  RequestedAmount:{lo:1}  RemainingAmount:{lo:1}  ExecutedAt:638223043314917445  Version:25  AccountId:11  ReportCause:NewOrder  InstructionId:{lo:5574686611683820165  hi:10500929413443338416}  ExternalOrderId:{lo:5574686611683820165  hi:10500929413443338416}  ExecutionEngineMarketId:1  OrderId:25  TxId:25
登录后复制

以上是proto:无法解析无效的有线格式数据的详细内容。更多信息请关注PHP中文网其他相关文章!

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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Debian OpenSSL有哪些漏洞 Debian OpenSSL有哪些漏洞 Apr 02, 2025 am 07:30 AM

OpenSSL,作为广泛应用于安全通信的开源库,提供了加密算法、密钥和证书管理等功能。然而,其历史版本中存在一些已知安全漏洞,其中一些危害极大。本文将重点介绍Debian系统中OpenSSL的常见漏洞及应对措施。DebianOpenSSL已知漏洞:OpenSSL曾出现过多个严重漏洞,例如:心脏出血漏洞(CVE-2014-0160):该漏洞影响OpenSSL1.0.1至1.0.1f以及1.0.2至1.0.2beta版本。攻击者可利用此漏洞未经授权读取服务器上的敏感信息,包括加密密钥等。

您如何使用PPROF工具分析GO性能? 您如何使用PPROF工具分析GO性能? Mar 21, 2025 pm 06:37 PM

本文解释了如何使用PPROF工具来分析GO性能,包括启用分析,收集数据并识别CPU和内存问题等常见的瓶颈。

您如何在GO中编写单元测试? 您如何在GO中编写单元测试? Mar 21, 2025 pm 06:34 PM

本文讨论了GO中的编写单元测试,涵盖了最佳实践,模拟技术和有效测试管理的工具。

Go语言中用于浮点数运算的库有哪些? Go语言中用于浮点数运算的库有哪些? Apr 02, 2025 pm 02:06 PM

Go语言中用于浮点数运算的库介绍在Go语言(也称为Golang)中,进行浮点数的加减乘除运算时,如何确保精度是�...

Go的爬虫Colly中Queue线程的问题是什么? Go的爬虫Colly中Queue线程的问题是什么? Apr 02, 2025 pm 02:09 PM

Go爬虫Colly中的Queue线程问题探讨在使用Go语言的Colly爬虫库时,开发者常常会遇到关于线程和请求队列的问题。�...

您如何在go.mod文件中指定依赖项? 您如何在go.mod文件中指定依赖项? Mar 27, 2025 pm 07:14 PM

本文讨论了通过go.mod,涵盖规范,更新和冲突解决方案管理GO模块依赖关系。它强调了最佳实践,例如语义版本控制和定期更新。

从前端转型后端开发,学习Java还是Golang更有前景? 从前端转型后端开发,学习Java还是Golang更有前景? Apr 02, 2025 am 09:12 AM

后端学习路径:从前端转型到后端的探索之旅作为一名从前端开发转型的后端初学者,你已经有了nodejs的基础,...

您如何在GO中使用表驱动测试? 您如何在GO中使用表驱动测试? Mar 21, 2025 pm 06:35 PM

本文讨论了GO中使用表驱动的测试,该方法使用测试用例表来测试具有多个输入和结果的功能。它突出了诸如提高的可读性,降低重复,可伸缩性,一致性和A

See all articles