0

我一直在使用 protobuf-net 通过网络发送一些对象,直到现在一切都运行良好。但是,我遇到了我的类的一个特定实例,它在单声道下运行时无法反序列化。完全相同的对象反序列化在 .net 下正确运行。byte[]我已经通过检查 md5 总和验证了当我在单声道和 .net 下运行时通过电线接收到的数据完全相同。这表明问题必须与 protobuf-net 反序列化有关。这是我用来反序列化的代码byte[]

using (MemoryStream ms = new MemoryStream(serializedByteArray)) 
{ 
    return (MyProtoBufDto)Serializer.Deserialize<MyProtoBufDto>(ms); 
}

这是我得到的例外:

System.IO.EndOfStreamException: Failed to read past end of stream.
at ProtoBuf.ProtoReader.Ensure (int,bool) <0x00167>
at ProtoBuf.ProtoReader.ReadString () <0x0005b>
at (wrapper dynamic-method) System.Collections.Generic.KeyValuePair`2<string, string>.proto_18 (object,ProtoBuf.ProtoReader) <0x000c8>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.ProtoReader.ReadTypedObject (object,int,ProtoBuf.ProtoReader,System.Type) <0x00056>
at ProtoBuf.ProtoReader.ReadObject (object,int,ProtoBuf.ProtoReader) <0x0001b>
at (wrapper dynamic-method) System.Collections.Generic.KeyValuePair`2<string, System.Collections.Generic.List`1<System.Collections.Generic.KeyValuePair`2<string, string>>>.proto_16 (object,ProtoBuf.ProtoReader) <0x00220>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.ProtoReader.ReadTypedObject (object,int,ProtoBuf.ProtoReader,System.Type) <0x00056>
at ProtoBuf.ProtoReader.ReadObject (object,int,ProtoBuf.ProtoReader) <0x0001b>
at (wrapper dynamic-method) MyGroupDto.proto_14 (object,ProtoBuf.ProtoReader) <0x001bc>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.ProtoReader.ReadTypedObject (object,int,ProtoBuf.ProtoReader,System.Type) <0x00056>
at ProtoBuf.ProtoReader.ReadObject (object,int,ProtoBuf.ProtoReader) <0x0001b>
at (wrapper dynamic-method) System.Collections.Generic.KeyValuePair`2<MyGroupNameDto, MyGroupDto>.proto_12 (object,ProtoBuf.ProtoReader) <0x00197>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.ProtoReader.ReadTypedObject (object,int,ProtoBuf.ProtoReader,System.Type) <0x00056>
at ProtoBuf.ProtoReader.ReadObject (object,int,ProtoBuf.ProtoReader) <0x0001b>
at (wrapper dynamic-method) MyResultProtoBufDto.proto_8 (object,ProtoBuf.ProtoReader) <0x002b9>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.ProtoReader.ReadTypedObject (object,int,ProtoBuf.ProtoReader,System.Type) <0x00056>
at ProtoBuf.ProtoReader.ReadObject (object,int,ProtoBuf.ProtoReader) <0x0001b>
at (wrapper dynamic-method) MyProtoBufDto.proto_6 (object,ProtoBuf.ProtoReader) <0x00116>
at ProtoBuf.Serializers.CompiledSerializer.ProtoBuf.Serializers.IProtoSerializer.Read (object,ProtoBuf.ProtoReader) <0x0002d>
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize (int,object,ProtoBuf.ProtoReader) <0x00112>
at ProtoBuf.Meta.TypeModel.DeserializeCore (ProtoBuf.ProtoReader,System.Type,object,bool) <0x0005b>
at ProtoBuf.Meta.TypeModel.Deserialize (System.IO.Stream,object,System.Type,ProtoBuf.SerializationContext) <0x00097>
at ProtoBuf.Meta.TypeModel.Deserialize (System.IO.Stream,object,System.Type) <0x0001f>
at ProtoBuf.Serializer.Deserialize<MyProtoBufDto> (System.IO.Stream) <0x00033>
at MyClient.Serialization.ProtoBufSerialization.DecompressAndDeserialize<MyProtoBufDto> (byte[]) <0x0008b>

我已经尝试过单声道 2.10.9 和最新的单声道 3.2.3 并且收到了同样的异常。我正在使用最新的 protobuf-net dll 版本(2.0.0.666)。我尝试了 protobuf-net-mono 目录中的 dll(用单声道编译器编译?)和常规的 .net 编译版本,但在单声道下运行时不断收到此异常。

课程详情MyProtoBufDto

[DataContract]
public class MyProtoBufDto
{
    [DataMember(Order = 1)]
    public List<MyResultProtoBufDto> Result { get; set; }
}

[DataContract]
public class MyResultProtoBufDto
{
    [DataMember(Order = 1)]
    public Dictionary<MyGroupNameDto, MyGroupDto> Groups { get; set; }
}

[DataContract]
public class MyGroupDto
{
    [DataMember(Order = 1)]
    public Dictionary<string, List<KeyValuePair<string, string>>> Group { get; set; }
}

MyGroupNameDto只是一个enum

更多信息: 这是在 linux 上使用单声道。我还没有在 Windows 上测试过单声道。我验证了 byte[] 离线和解压后的 byte[] 在单声道和 Windows 下是相同的,因此我们应该向 protobuf-net 提供相同的确切数据。

更新 我们在服务器端进行了更改,以始终从已序列化的对象中删除空集合,此后未遇到此错误。我知道 protobuf 没有区分空集合和空集合,但奇怪的是,mono 与 .net 上的行为以某种方式有所不同,并引发了此错误。

4

3 回答 3

1

这很有趣。我可以问:单声道是什么?windows上的单声道?linux上的单声道?我可以尝试复制,但是您可以给我的任何东西以确保我们正在寻找相同的东西真的会帮助我。事实上,如果可能的话,真正有帮助的是“这是我试图反序列化的数据的 base-64”(即serializedByteArray)——这将使我能够非常快速地确定是数据还是反序列化器有问题。或者,也许一些“这里的一些代码会生成一些发明的数据,然后不会反序列化”样本?

为了解释错误的含义:有些东西声明了“我需要 {n} 个字节”——在这种情况下ReadString。它尝试将其加载到内部缓冲区中,Stream但没有提供足够的数据。

我也有点担心DecompressAndDeserialize调用堆栈:压缩当然没有任何问题 - 但是当您进行“我们是否获得正确的数据”检查时,是您解压缩之前吗?或之后?一种可能性是产生不同结果的是减压层。

乐于调查;但是如果你能提供更多的上下文/样本会节省很多时间——如果不能在公共网站上完成,那么可能通过电子邮件等?

于 2013-09-26T07:17:47.833 回答
0

两个端点是否都在带有 protobuf-net 的 .NET 环境中运行,还是其中一个不同(例如,使用 C++ 库)?

使用通过套接字从 C++ 应用程序 (Linux) 发送的 protobuf-net (Windows) 反序列化消息时,我曾经遇到过同样的问题。

我的消息如下所示:

message Envelope
{
  required int32 type = 1;  
  required bytes data = 2;  
}

在 C++ 方面,我设置了“数据”(嵌入的消息)而不传递大小,即

envelope->set_data(buf);

代替

envelope->set_data(buf, bufSz);

这意味着不仅序列化消息的大小错误(信封-> ByteSize()),而且缓冲区最后还包含垃圾,这可能是 protobuf-net 将其解释为额外字段并继续解析超出其应有的内容的原因.

值得检查它是否符合相同的路线。

于 2013-09-26T15:00:26.610 回答
0

我能够找出这个奇怪问题的原因。显然我在最初调试这个问题时出错了。我曾认为在.net 和单声道下运行时发送反序列化的 byte[] 是相同的。但是,情况并非如此,错误在于将 DeflateStream 与反序列化结合使用。在单声道下运行时,DeflateStream 没有返回 protobuf-net 期望的所有字节。

损坏的代码如下所示:

using (MemoryStream ms = new MemoryStream(compressedByteArray))
using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress, true))
     return (MyProtoBufDto)Serializer.Deserialize<MyProtoBufDto>(ds);

工作代码如下所示:

using (MemoryStream msDecompressed = new MemoryStream())
{
     using (MemoryStream ms = new MemoryStream(compressedByteArray))
     using (DeflateStream ds = new DeflateStream(ms , CompressionMode.Decompress, true))
           ds.CopyTo(msDecompressed);

     msDecompressed.Seek(0, SeekOrigin.Begin);
     return (MyProtoBufDto)Serializer.Deserialize<MyProtoBufDto>(msDecompressed);
}

单声道和 .net 之间存在这种差异真是太糟糕了。我了解 DeflateStream 使用底层缓冲区。Mono 可能正在使用延迟实现,即使请求更多字节,也只返回缓冲区中的解压缩字节。只要 DeflateStream.ReadBytes 返回至少 1 个字节,这在技术上可能不会破坏任何 .net 规范。对 protobuf-net 的更改在未获得预期数字时继续从流中读取字节可能会解决此问题。只有当 ReadBytes 返回零字节时,protobuf-net 才会抛出我们看到的异常。

于 2013-10-09T21:15:08.180 回答