在反序列化过程中出现下一个异常:“源数据中的无效字段: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<byte>
或类似的值来表示超大缓冲区的部分;即
new ArraySegment<byte>(ms.GetBuffer(), 0, (int)ms.Length);
您使用BinaryFormatter
逃脱的原因是BinaryFormatter
知道它自己的长度。协议缓冲区是一种可附加格式,因此它不知道自己的长度。
【讨论】:
以上是关于在反序列化过程中出现下一个异常:“源数据中的无效字段:0”。如何找出源代码中的原因/错误位置?的主要内容,如果未能解决你的问题,请参考以下文章
ProtoBuf 在反序列化期间损坏字节数组(添加了额外的 0)