在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?

Posted

技术标签:

【中文标题】在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?【英文标题】:I get next exception during deserialization: "Invalid field in source data: 0". How to figure out the reason/wrong place in source code? 【发布时间】:2012-09-26 11:54:12 【问题描述】:

在这个函数中,我尝试在客户端反序列化class Simulator

private void OnHandshakeSimulationStart(Peer peer, Message msg, int seq)
    
        System.Diagnostics.Trace.WriteLine("TRY OnHandshakeSimulationStart id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        try
        
            HandshakeSimulationStart simstart = msg as HandshakeSimulationStart;
            if (simstart == null) OnHandshakeFailed();
            System.Diagnostics.Trace.WriteLine("#1.1 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);

            //Simulator sim = (Simulator)simstart.SimulationState.GetObject();
            Simulator sim = null;
            System.Diagnostics.Trace.WriteLine("#1.2 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);

            try
            
               // sim = (Simulator)simstart.SimulationState.GetObject();
                using (MemoryStream ms = (MemoryStream)simstart.SimulationState.GetObjectWoDeserialize())
                
                    System.Diagnostics.Trace.WriteLine("#2.1 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                    ms.Seek(0, SeekOrigin.Begin);
                    System.Diagnostics.Trace.WriteLine("#2.2 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                    sim = Serializer.Deserialize<Simulator>(ms); //(Simulator)new BinaryFormatter().Deserialize(ms);
                    System.Diagnostics.Trace.WriteLine("#2.3 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                
            
            catch (SerializationException e)
            
                Console.WriteLine("Failed to deserialize. Reason: " + e.Message);                    
            

            System.Diagnostics.Trace.WriteLine("#3 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            sim.PostDeserialize();
            System.Diagnostics.Trace.WriteLine("#4 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            lock (this)
            
                simulator = sim;
                // команды полученные до этого ставим в очередь
                foreach (Commands.Command cmd in CollectedCommandsDuringHandshake)
                    if (cmd.tact >= simulator.tactCounter)
                        simulator.InternalQueue(cmd);
                System.Diagnostics.Trace.WriteLine("#5 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                CollectedCommandsDuringHandshake = null;

                // настраиваем сетевое время
                networkTime = new Network.Timer(simulator.HZ);
                // настраиваем обработчик команд
                simulator.OnAfterCommand.Add(typeof(Commands.Command), new CommandHandler(OnAfterCommand));
                System.Diagnostics.Trace.WriteLine("#6 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                // хендшейк закончен
                OnHandshakeCompleted();
                System.Diagnostics.Trace.WriteLine("#7 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
                CurrentReceiveHandler = new Peer.MessageReceivedHandler(OnMessageReceivedInActiveState);
                System.Diagnostics.Trace.WriteLine("#8 id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
            
        
        catch (Exception exception)
        
            System.Diagnostics.Trace.WriteLine("OnHandshakeSimulationStart ERROR: " + exception.Message + " id = " + +System.Threading.Thread.CurrentThread.ManagedThreadId);                
        
    

通过使用 DebugView.exe 应用程序,我得到:

[26480] Client handshaked: id=0 name=ECDIS[1] version=3.8.1 
[26480] ObjectWrapper(object obj) - ms.Length = 67 id = 10 
[26480] Write(Network.Packet packet) - ObjectDumpProtoBuf.Length = 256 id = 10 
[25404] ObjectWrapper(Network.Packet packet) - len = 256 id = 5 
[25404] TRY OnHandshakeSimulationStart id = 5 
[25404] #1.1 id = 5 
[25404] #1.2 id = 5 
[25404] GetObjectWoDeserialize() id = 5 
[25404] #2.1 id = 5 
[25404] #2.2 id = 5 
[25404] OnHandshakeSimulationStart ERROR: Invalid field in source data: 0 id = 5 

我发现反序列化期间引发的异常class Simulator

namespace Trainer

[Serializable]
[ProtoContract]
public class Simulator


    [ProtoMember(1)]
    public IDictionary<int, Task> tasks = new Dictionary<int, Task>();

    [ProtoMember(2)]
    public Workplace[] workplaces;

    [ProtoMember(3)]
    public int tactCounter = 0;

    [ProtoMember(4)]
    public int tactLimit = 0;

    [NonSerialized]
    public int sheduleLimit = 0;

    [ProtoMember(5)]
    public int HZ = 100; 

    public int SyncFactor  get  return 1;   // HZ/8

    public double DT  get  return 1.0 / (double)HZ;  

    [ProtoMember(6)]
    List<Commands.Command> commandQueue = new List<Commands.Command>();
    //Queue commandQueue = new Queue();

    [NonSerialized]
    public CommandEventTable OnBeforeCommand = new CommandEventTable();

    [NonSerialized]
    public CommandEventTable OnAfterCommand = new CommandEventTable();

    public void PostDeserialize()
    
        OnBeforeCommand = new CommandEventTable();
        OnAfterCommand = new CommandEventTable();
        foreach (Task t in tasks.Values)
            t.PostDeserialize();
    

    public Simulator()
    

    

//there are also a lot of functions...


一个对象包装器有助于序列化/反序列化,它可以与 binaryformatter 一起正常工作,但我将其更改为使用 protobuf-net:

namespace Trainer.Network

    [Serializable]
    [ProtoContract]
    public class ObjectWrapper : IFile
    
    //public byte[] ObjectDump;

    [ProtoMember(1)]
    public byte[] ObjectDumpProtoBuf;

    public ObjectWrapper(object obj)
    
        using (MemoryStream ms = new MemoryStream())
        
            //for protobuf-net:
            Serializer.Serialize(ms, obj);
            ObjectDumpProtoBuf = ms.GetBuffer();

            //for binaryformatter:
            //new BinaryFormatter().Serialize(ms,obj);
            //ObjectDump = ms.GetBuffer();

            _Guid = Guid.NewGuid();

            System.Diagnostics.Trace.WriteLine("ObjectWrapper(object obj) - ms.Length = " + ms.Length + " id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        
    

    public ObjectWrapper(Network.Packet packet)
    
        bool body = packet.ReadBool();
        if (body)
        
            int len = packet.ReadInt();
            ObjectDumpProtoBuf = (byte[])packet.Read(typeof(byte), len);
            //ObjectDump = (byte[])packet.Read(typeof(byte),len);

            System.Diagnostics.Trace.WriteLine("ObjectWrapper(Network.Packet packet) - len = " + len + " id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        
        else
            _Guid = (Guid)packet.Read(typeof(Guid));

    

    public void Write(Network.Packet packet)
    
        System.Diagnostics.Trace.WriteLine("Write(Network.Packet packet) - ObjectDumpProtoBuf.Length = " + ObjectDumpProtoBuf.Length + " id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        packet.Write(true);

        //packet.Write((int)ObjectDump.Length);
        //packet.Write(ObjectDump);           
        packet.Write((int)ObjectDumpProtoBuf.Length);
        packet.Write(ObjectDumpProtoBuf);
    

    public void WriteFileRef(Network.Packet packet)
    
        packet.Write(false);
        packet.Write(Guid);
    

    public object GetObject()
    
        //using (MemoryStream ms = new MemoryStream(ObjectDump))
        using (MemoryStream ms = new MemoryStream(ObjectDumpProtoBuf))
            return new BinaryFormatter().Deserialize(ms);
    

    public MemoryStream GetObjectWoDeserialize()
    
        System.Diagnostics.Trace.WriteLine("GetObjectWoDeserialize() id = " + System.Threading.Thread.CurrentThread.ManagedThreadId);
        return new MemoryStream(ObjectDumpProtoBuf);
    

    #region IFile Members

    [ProtoMember(2)]
    public Guid _Guid = new Guid();
    public Guid Guid  get  return _Guid;  

    public byte[] Data
    
        //get  return ObjectDump;  
        //set  ObjectDump = value; 
        get  return ObjectDumpProtoBuf; 
        set  ObjectDumpProtoBuf = value; 
    
    #endregion

问题:如何找出问题的确切位置?我的意思是“源数据中的无效字段:0”源数据 0 是什么? 这是一个

   [ProtoMember(1)]
    public IDictionary<int, Task> tasks = new Dictionary<int, Task>();

还是没有? 所以我不明白出了什么问题......

【问题讨论】:

看看这个之前的问题:***.com/questions/10401464/… 【参考方案1】:

我们开始吧:

ObjectDumpProtoBuf = ms.GetBuffer();

这为您提供了 oversized 后备缓冲区。如果您想避免内存复制,这很方便,但是您需要非常小心,只处理其中的 ms.Length 字节 - 其余的都是垃圾。特别是,由于您没有使用该内存,它将全为零......并且零不是有效的字段标题,并且会导致您看到的错误。

要快速检查是否是问题所在,请将其替换为:

ObjectDumpProtoBuf = ms.ToArray();

这是缓冲区中数据的大小合适的副本。

如果可行,您可能想要切换到返回ArraySegment&lt;byte&gt; 或类似的值来表示超大缓冲区的部分;即

new ArraySegment<byte>(ms.GetBuffer(), 0, (int)ms.Length);

您使用BinaryFormatter 逃脱的原因是BinaryFormatter 知道它自己的长度。协议缓冲区是一种可附加格式,因此它知道自己的长度。

【讨论】:

以上是关于在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?的主要内容,如果未能解决你的问题,请参考以下文章

C#:是否可以从属性集中确定当前是否处于反序列化模式?

ProtoBuf 在反序列化期间损坏字节数组(添加了额外的 0)

Spring数据redis在反序列化List类型时返回null

Gson 在反序列化对象时忽略 null

在反序列化时使用动态和强制执行 WCF 合同

用于在反序列化期间忽略未知属性的 SpringMVC 全局设置