在此函数中,我尝试在客户端反序列化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...
}
一个Object包装器有助于序列化/反序列化,并且它可以正常使用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>();
是不是?
所以我无法理解有什么不对......
答案 0 :(得分:1)
我们走了:
ObjectDumpProtoBuf = ms.GetBuffer();
它为您提供超大后备缓冲区。如果你想避免内存复制,这很方便,但你需要非常小心,只处理ms.Length
个字节 - 剩下的就是垃圾。特别是,由于你没有使用那个内存,它将全部为零......并且零不是有效的字段标题,并且会导致你看到的错误。
要快速检查是否存在问题,请将其替换为:
ObjectDumpProtoBuf = ms.ToArray();
这是来自缓冲区的正确大小的数据副本。
如果可以,可能想切换到返回ArraySegment<byte>
或类似代表超大缓冲区的部分;即。
new ArraySegment<byte>(ms.GetBuffer(), 0, (int)ms.Length);
BinaryFormatter
使用它的原因是BinaryFormatter
知道自己的长度。协议缓冲区是一种可附加格式,因此它不知道它自己的长度。