私はクライアントサーバーアプリケーションを実装しており、データをシリアル化して送信するためのさまざまな方法を検討しています。私はXmlSerializersを使い始めました。これはかなりうまく機能しましたが、データの生成が遅く、特にネット経由で送信する必要がある場合は大きなオブジェクトを作成します。そこで、Protobufとprotobuf-netを調べ始めました。
私の問題は、protobufが型情報を送信しないという事実にあります。Xmlシリアライザーを使用すると、Xmlにシリアル化されたオブジェクトにオブジェクトの型名が含まれているため、同じストリームでさまざまな(シリアル化可能な)オブジェクトを送受信するラッパーを作成できました。
ObjectSocket socket = new ObjectSocket();
socket.AddTypeHandler(typeof(string)); // Tells the socket the types
socket.AddTypeHandler(typeof(int)); // of objects we will want
socket.AddTypeHandler(typeof(bool)); // to send and receive.
socket.AddTypeHandler(typeof(Person)); // When it gets data, it looks for
socket.AddTypeHandler(typeof(Address)); // these types in the Xml, then uses
// the appropriate serializer.
socket.Connect(_host, _port);
socket.Send(new Person() { ... });
socket.Send(new Address() { ... });
...
Object o = socket.Read();
Type oType = o.GetType();
if (oType == typeof(Person))
HandlePerson(o as Person);
else if (oType == typeof(Address))
HandleAddress(o as Address);
...
私はこれに対するいくつかの解決策を検討しました。これには、私のソケットを介して送信される唯一のタイプのオブジェクトであるマスター「状態」タイプクラスの作成が含まれます。ただし、これはXmlシリアライザーで使用していた機能から離れているため、その方向性は避けたいと思います。
2番目のオプションは、オブジェクトのタイプを定義するあるタイプのラッパーでprotobufオブジェクトをラップすることです。(このラッパーには、パケットIDや宛先などの情報も含まれます。)protobuf-netを使用してオブジェクトをシリアル化し、そのストリームをXmlタグの間に貼り付けるのはばかげているようですが、私はそれを検討しました。この機能をprotobufまたはprotobuf-netから取り出す簡単な方法はありますか?
私は3番目の解決策を考え出し、それを以下に投稿しましたが、より良い解決策があれば、それも投稿してください!
フィールド境界のバグに関する情報(を使用 System.String
):
ハッシュ:
protected static int ComputeTypeField(Type type) // System.String
{
byte[] data = ASCIIEncoding.ASCII.GetBytes(type.FullName);
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
return Math.Abs(BitConverter.ToInt32(md5.ComputeHash(data), 0));
}
シリアル化:
using (MemoryStream stream = new MemoryStream())
{
Serializer.NonGeneric.SerializeWithLengthPrefix
(stream, o, PrefixStyle.Base128, field); // field = 600542181
byte[] data = stream.ToArray();
_pipe.Write(data, 0, data.Length);
}
デシリアリゼーション:
using (MemoryStream stream = new MemoryStream(_buffer.Peek()))
{
lock (_mapLock)
{
success = Serializer.NonGeneric.TryDeserializeWithLengthPrefix
(stream, PrefixStyle.Base128, field => _mappings[field], out o);
}
if (success)
_buffer.Clear((int)stream.Position);
else
{
int len;
if (Serializer.TryReadLengthPrefix(stream, PrefixStyle.Base128, out len))
_buffer.Clear(len);
}
}
field => _mappings[field]
KeyNotFoundException
を探している間スローし63671269
ます。
ハッシュ関数でに置き換えるToInt32
とToInt16
、フィールド値がに設定され29723
て機能します。System.String
のフィールドをに明示的に定義した場合にも機能し1
ます。フィールドを明示的に定義する600542181
と、ハッシュ関数を使用してフィールドを定義するのと同じ効果があります。シリアル化される文字列の値は結果を変更しません。