直列化復元不明のタイプprotobuf-net
-
21-08-2019 - |
質問
私は2つのネットワークアプリにすべきであると直列化されたprotobuf-netメッセージです。まserializeのオブジェクトに送信し できないのでよいのかを直列化復元の受信バイト.
た直列化復元ことができるNullReferenceException.
// Where "ms" is a memorystream containing the serialized
// byte array from the network.
Messages.BaseMessage message =
ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms);
私通ヘッダーの前に直列化されたバイトを含むメッセージタイプIDで利用できる巨大スイッチ計算書の返却期待sublcassタイプです。のブロック以下、そのエラー:システム。反射を生み出します。TargetInvocationException--->のシステム。NullReferenceException.
//Where "ms" is a memorystream and "messageType" is a
//Uint16.
Type t = Messages.Helper.GetMessageType(messageType);
System.Reflection.MethodInfo method =
typeof(ProtoBuf.Serializer).GetMethod("Deserialize").MakeGenericMethod(t);
message = method.Invoke(null, new object[] { ms }) as Messages.BaseMessage;
この機能を使っていメッセージを送信するためのネットワーク
internal void Send(Messages.BaseMessage message){
using (System.IO.MemoryStream ms = new System.IO.MemoryStream()){
ProtoBuf.Serializer.Serialize(ms, message);
byte[] messageTypeAndLength = new byte[4];
Buffer.BlockCopy(BitConverter.GetBytes(message.messageType), 0, messageTypeAndLength, 0, 2);
Buffer.BlockCopy(BitConverter.GetBytes((UInt16)ms.Length), 0, messageTypeAndLength, 2, 2);
this.networkStream.Write(messageTypeAndLength);
this.networkStream.Write(ms.ToArray());
}
}
このクラスは、基底クラスです、私は直列化さ:
[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
[ProtoMember(1)]
abstract public UInt16 messageType { get; }
}
[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
[ProtoMember(1)]
public override UInt16 messageType
{
get { return 1; }
}
}
固定 使用マークGravellた。取り出して、そのProtoMemberから属性を読み取り専用になります。ものへ切り替えを使用SerializeWithLengthPrefix.ここでの私の現在:
[Serializable,
ProtoContract,
ProtoInclude(50, typeof(BeginRequest))]
abstract internal class BaseMessage
{
abstract public UInt16 messageType { get; }
}
[Serializable,
ProtoContract]
internal class BeginRequest : BaseMessage
{
public override UInt16 messageType
{
get { return 1; }
}
}
を受けるオブジェクト:
//where "this.Ssl" is an SslStream.
BaseMessage message =
ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
this.Ssl, ProtoBuf.PrefixStyle.Base128);
送信オブジェクト:
//where "this.Ssl" is an SslStream and "message" can be anything that
// inherits from BaseMessage.
ProtoBuf.Serializer.SerializeWithLengthPrefix<BaseMessage>(
this.Ssl, message, ProtoBuf.PrefixStyle.Base128);
解決
まず、ネットワークの使用状況のために、(必要に応じてタグで)あなたのための長さを扱うSerializeWithLengthPrefix
とDeserializeWithLengthPrefix
があります。 MakeGenericMethod
は一見OKになります。それが簡単にデシリアライズするために作るために型にタグを解決するために、(基本的に)has an override of DeserializeWithLengthPrefix
を取る保留コードFunc<int,Type>
:これは実際に保留中のIは、RPCスタックを実装するために行ってきた仕事のコミットに非常に密接に結び付けオンザフライでの予期しないデータます。
メッセージタイプが実際にBaseMessage
とBeginRequest
間の継承に関連する場合、あなたはこれを必要としません。常に階層の最上位の契約タイプにそれを行くとダウン(何らかのワイヤー詳細に)そのように動作します。
また、 - 私はそれをテストする機会がなかったが、以下はそれをひっくり返すことがあります:
[ProtoMember(1)]
public override UInt16 messageType
{
get { return 1; }
}
これは、シリアル化が設定されますが、値を設定するためのメカニズムを持っていません。多分これは問題がありますか?私がいないので、ここで[ProtoMember]
を削除してみてください、これは便利です - それは(限りシリアライズが懸念している)である、[ProtoInclude(...)]
マーカーの大部分が重複
他のヒント
Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks, Marc.
または
RuntimeTypeModel.Default.Deserialize(Stream, null, Type);
これを処理する別の方法は「重労働」のためにいるProtobufネットを使用することではなく、独自のメッセージ・ヘッダーを使用することです。処理ネットワークメッセージの問題点は、境界を越えて破壊することができるということです。これは、一般的に読み蓄積するバッファを使用する必要があります。あなたがあなた自身のヘッダーを使用する場合は、メッセージがいるProtobuf-netのためにそれを渡す前にその全体が存在していることを確認することができます。
の例としては:
送信するには、
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
MyMessage message = new MyMessage();
ProtoBuf.Serializer.Serialize<BaseMessage>(ms, message);
byte[] buffer = ms.ToArray();
int messageType = (int)MessageType.MyMessage;
_socket.Send(BitConverter.GetBytes(messageType));
_socket.Send(BitConverter.GetBytes(buffer.Length));
_socket.Send(buffer);
}
受信するには、
protected bool EvaluateBuffer(byte[] buffer, int length)
{
if (length < 8)
{
return false;
}
MessageType messageType = (MessageType)BitConverter.ToInt32(buffer, 0);
int size = BitConverter.ToInt32(buffer, 4);
if (length < size + 8)
{
return false;
}
using (MemoryStream memoryStream = new MemoryStream(buffer))
{
memoryStream.Seek(8, SeekOrigin.Begin);
if (messageType == MessageType.MyMessage)
{
MyMessage message =
ProtoBuf.Serializer.Deserialize<MyMessage>(memoryStream);
}
}
}
十分なデータがあるまで後者の方法は、アキュムレータバッファの「試み」であろう。サイズ要件が満たされたら、メッセージをデシリアライズすることができる。