protobuf-net反序列化单声道下的System.IO.EndOfStreamException

时间:2013-09-26 03:04:59

标签: mono deserialization protobuf-net

我一直在使用protobuf-net通过线路发送一些对象,直到现在一切都运行良好。但是,我遇到了我的类的特定实例,在单声道下运行时无法反序列化。完全相同的对象反序列化在.net下正确运行。我已经验证,当我通过检查md5总和在单声道和.net下运行时,我通过线路接收到的是完全相同的byte[]。这表明问题必须与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编译版本但在mono下运行时仍然遇到这个异常。

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上使用mono。我尚未在Windows上测试单声道。在单声道和窗口下解压缩后,我验证了线路上的字节[]和字节[],因此我们应该为protobuf-net提供相同的精确数据。

更新 我们在服务器端进行了更改,以便始终从序列化的对象中删除空集合,从那时起就没有遇到此错误。我理解protobuf并没有区分空集合和空集合,但仍然有一点特别是在mono和.net上行为不同,它触发了这个错误。

3 个答案:

答案 0 :(得分:1)

这很有趣。我可以问:单声道关于什么?在Windows上单声道? Linux上的单声道?我可以尝试重复,但你可以给我任何东西,以确保我们看到同样的事情真的会帮助我。确实,如果可能的话真正帮助的是“这里是我试图反序列化的数据的基础64”(即serializedByteArray) - 这将使我能够非常快速地确定是否它是数据解串器的错误。或者,也许有些“这里有一些代码可以生成一些发明的数据然后不反序列化”样本?

解释错误意味着什么:某些事情声明了“我需要{n}字节” - 在这种情况下是ReadString。它试图将其加载到内部缓冲区中,Stream没有给它足够的数据。

我也对调用堆栈中的DecompressAndDeserialize略感担忧:压缩当然没有任何错误 - 但是当你做“我们得到了正确的数据”检查时,那是 之前解压缩?或之后?一种可能性是解压缩层产生不同的结果。

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

答案 1 :(得分: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);

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

值得检查是否有相同的内容。

答案 2 :(得分:0)

我能够弄清楚这个奇怪问题的原因。显然我在最初调试此问题时出错了。我原以为我在发送用于反序列化的字节[]在.net和mono下运行时是相同的。但是,情况并非如此,错误在于将DeflateStream与反序列化结合使用。在mono下运行时,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才会抛出我们看到的异常。