【问题标题】:protobuf-net deserialization System.IO.EndOfStreamException under monoprotobuf-net 反序列化 System.IO.EndOfStreamException 下 mono
【发布时间】:2013-09-26 03:04:59
【问题描述】:

我一直在使用 protobuf-net 通过网络发送一些对象,直到现在一切都运行良好。但是,我遇到了我的类的一个特定实例,它在单声道下运行时无法反序列化。完全相同的对象反序列化在 .net 下正确运行。我已经通过检查 md5 总和验证了当我在 mono 和 .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 编译版本,但在单声道下运行时不断出现此异常。

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 上的行为以某种方式有所不同,并引发了此错误。

【问题讨论】:

    标签: mono deserialization protobuf-net


    【解决方案1】:

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

    解释错误的含义:有东西声明了“我需要 {n} 个字节”——在本例中为 ReadString。它尝试将其加载到内部缓冲区中,而 Stream 没有给它足够的数据。

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

    乐于调查;但是,如果您可以提供更多上下文/示例,这将节省大量时间 - 如果无法在公共网站上完成,那么可能通过电子邮件等方式完成?

    【讨论】:

    • 感谢马克的快速回复。我更新了我的问题,为您提供了更多信息。我应该能够在接下来的几天内将那个 byte[] 给你。
    • 嗨,马克。我们在服务器端进行了更改,以始终从序列化的对象中删除空集合,从那时起我就无法重现此错误。有趣的是,mono 和 .net 上的行为是不同的。很抱歉,我无法提供该字节 [] 供您进行故障排除。
    【解决方案2】:

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

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

    我的消息如下所示:

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

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

    envelope->set_data(buf);
    

    而不是

    envelope->set_data(buf, bufSz);
    

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

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

    【讨论】:

    • 序列化始终在 .net 上。反序列化我在 .net 和 linux 下的 mono 上测试过。只有单声道有问题。在反序列化之前,我已经验证了单声道和 windows 上的实际 byte[] 是相同的,所以我认为这不是套接字问题。
    【解决方案3】:

    我能够找出这个奇怪问题的原因。显然我在最初调试这个问题时出错了。在.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);
    }
    

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

    【讨论】:

      猜你喜欢
      • 2012-04-26
      • 2012-11-16
      • 1970-01-01
      • 2014-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多