2

使用 List 作为 protobuf 中重复字段的对应项是否正确?我正在尝试这个并且总是遇到异常:

线型无效;这通常意味着您在没有截断或设置长度的情况下覆盖了文件;看到使用 Protobuf-net,我突然得到一个关于未知线型的异常

整个缓冲区是(第一个消息,下一个到达的附加到这个):9 8 5 26 5 24 238 98 32 1

java protobuf 文件:

package XXX;

option java_package = "XXX";
option java_outer_classname = "Protos";

option optimize_for = SPEED;

message V3DDelta {
  optional int32 bid = 1;
  optional int32 bidSize = 2;
  optional int32 ask = 3;
  optional int32 askSize = 4;
}

message Request {
  optional int32 type = 1;
  optional string request = 2;
}

message Response {
  optional int32 type = 1;
  optional string response = 2;
  repeated V3DDelta v3dDelta = 3;
}

和 protbuf-net 类:

[ProtoContract]
public class V3DDelta {
    [ProtoMember(1)]
    public double bid { get; set; }
    [ProtoMember(2)]
    public int bidSize { get; set; }
    [ProtoMember(3)]
    public double ask { get; set; }
    [ProtoMember(4)]
    public int askSize { get; set; }
}

[ProtoContract]
public class Request {
    [ProtoMember(1)]
    public int Type { get; set; }
    [ProtoMember(2)]
    public string Rq { get; set; }
}

[ProtoContract]
public class Response {
    [ProtoMember(1)]
    public int Type { get; set; }
    [ProtoMember(2)]
    public string Rsp { get; set; }
    [ProtoMember(3)]
    public List<V3DDelta> v3dDelta { get; set; }
    public Response() {
        v3dDelta = new List<V3DDelta>();
    }
}

我试过 V3DDelta[] 但结果是一样的。阅读留言:

Response rsp = Serializer.DeserializeWithLengthPrefix<Response>(rcvstream, PrefixStyle.Base128);

并在 java 中使用 writeDelimitedTo 发送消息。c# 中的缓冲区与 java 中的缓冲区完全相同。当有 na v3dDelta 字段时,一切都按预期工作。

4

1 回答 1

2

是的,或者List<T>数组 ( T[]) 将适用于repeated. 顺便说一句,有一个工具可以从 .proto 定义中生成 protobuf-net 类。

您正在尝试“使用长度前缀”阅读它,但是: 9不能作为 varint 前缀 ( 9,作为字段标头,表示“字段 1,固定 64 位数据”,但是:在这种情况下,它应该是变量)。

实际上,您的任何数据都不兼容9作为字段标头,因为您的定义中没有任何 64 位值作为 field 1。您确实有一个doubleas field 3,它可以很好地完成这项工作 - 但是,这将是73一个字段标题。

我会告诉你序列 9,8,5,... 代表什么,但是 - 我们会有:

9 : field 1, fixed 64-bit
    8 5 26 5 24 238 98 32 <== payload for above
1 : field 0, fixed 64-bit
    ^^ not *really* valid, but fields <= 0 generally mean "stop" - but
       frankly this is not a clean/defined/expected exit condition

再说一遍:请检查您的数据。这看起来不像一个 protobuf 流,或者至少不是一个与您的架构匹配的流。


编辑:可能java writeDelimitedTo包含长度,没有标题)使其在技术上不是一致的protobuf文件,但是......嗯),所以让我们调查一下:

int len = ProtoReader.DirectReadVarintInt32(ms);

这给了我们9,我们还有9字节,所以看起来不错......

8 : Field 1, varint
  5 = payload of above
26 : Field 3, length-delimited
  5 = length of payload
   24 238 98 32 1 = payload of ^^^
      24 : Field 3, varint
          238, 98 = payload of ^^^ = 12654
      32 : Field 4, varint
          1 = payload of ^^^ = 1

现在看起来它应该解析......调查为什么它不是......


编辑2:经过更多调试,部分原因是您(很抱歉)破坏了V3DDelta属性。您将它们定义为int32原型(并且字段 3 是数据中的变量),但您将它们实现为double... 并且double不是int32朋友。

所以:

[ProtoContract]
public class V3DDelta
{
    [ProtoMember(1)]
    public int bid { get; set; }
    [ProtoMember(2)]
    public int bidSize { get; set; }
    [ProtoMember(3)]
    public int ask { get; set; }
    [ProtoMember(4)]
    public int askSize { get; set; }
}

然后以下工作正常:

using (var ms = new MemoryStream(buffer))
{
    int len = ProtoReader.DirectReadVarintInt32(ms);
    var resp = (Response)model.Deserialize(ms, null, typeof(Response), len);

    Assert.AreEqual(5, resp.Type);
    Assert.AreEqual(1, resp.v3dDelta.Count);
    Assert.AreEqual(12654, resp.v3dDelta[0].ask);
    Assert.AreEqual(1, resp.v3dDelta[0].askSize);
}

从技术上讲,我可以让 protobuf-net 接受 avarint作为double值,但这高度表明模式并不真正匹配,所以我认为正确的做法是在这种情况下更改类型。

于 2012-06-18T12:48:01.103 回答