Wie kann ich senden mehrere Typen von Objekten über Protobuf?
Ich bin Implementierung einer client-server-Anwendung, und bin auf der Suche in verschiedener Weise zu serialisieren und übertragen der Daten. Ich begann die Arbeit mit Xml-Serialisierungsprogramme, das klappte ziemlich gut, aber Daten generieren, langsam, und machen große Objekte, vor allem, wenn Sie benötigen, um das senden über das Netz. Also suchte ich in Protobuf, und protobuf-net.
Mein problem liegt in der Tatsache, dass protobuf nicht gesendet Art Informationen mit sich. Mit Xml-Serialisierungsprogramme, ich war in der Lage zu bauen ein wrapper, der würde senden und empfangen verschiedene (serializable) - Objekt über den gleichen stream, da Objekt in Xml serialisiert enthalten, die den Typnamen des Objekts.
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);
...
Habe ich mir überlegt, ein paar Lösungen zu diesem, einschließlich der Erstellung einer master - "Staat" - Typ-Klasse, die ist die einzige Art von Objekt gesendet über mein socket. Dieser bewegt sich Weg von der Funktionalität, die ich gearbeitet habe, mit Xml-Serialisierungsprogramme, obwohl, so würde ich mag, um zu vermeiden, dass Richtung.
Die zweite option wäre, um wrap-protobuf-Objekte in eine Art von wrapper definiert, die den Typ des Objekts. (Dieser wrapper würden auch Daten enthalten, wie z.B. Paket-ID, und das Ziel.) Es scheint albern zu verwenden protobuf-net die Serialisierung eines Objekts, dann kleben Sie, dass Strom zwischen den Xml-tags, aber ich hab mir überlegt es. Gibt es eine einfache Möglichkeit um diese Funktionalität out-of-protobuf oder protobuf-net?
Habe ich eine Dritte Lösung, und die es gepostet unten, aber wenn Sie ein besseres haben, poste es bitte auch!
Informationen auf Feld-bounds-Fehler (mit System.String
):
Hashing:
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));
}
Serialisierung:
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);
}
Deserializaion:
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]
wirft einen KeyNotFoundException
bei der Suche nach 63671269
.
Wenn ich ersetzen ToInt32
mit ToInt16
im hash-Funktion, die das Feld Wert eingestellt ist 29723
und es funktioniert. Es funktioniert auch, wenn ich explizit definieren System.String
's Feld zu 1
. Explizit definieren das Feld 600542181
hat die gleiche Wirkung wie die Verwendung der hash-Funktion zu definieren. Den Wert der Zeichenfolge serialisiert wird, ändert nicht das Ergebnis.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Diese Funktionalität wird tatsächlich gebaut, wenn auch nicht offensichtlich.
In diesem Szenario, es ist abzusehen, dass Sie Kennzeichnen eine eindeutige Nummer pro message-Typ. Die überlastung, die Sie verwenden, übergibt Sie alle als "Feld 1", aber es ist eine überlastung, mit der Sie auch diese zusätzlichen header-Informationen (ist es immer noch der job des aufrufenden Codes, um zu entscheiden, wie die Zuordnung von zahlen zu Arten, obwohl). Sie können dann angeben, verschiedene Arten, wie verschiedene Felder der stream (Hinweis: dies funktioniert nur mit der Basis-128 Präfix-Stil).
Werde ich brauchen, um zu überprüfen, aber die Absicht ist, dass so etwas wie die folgende sollten funktionieren:
Beachten Sie, dass diese nicht derzeit arbeiten in der v2 bauen (da die "WithLengthPrefix" code ist unvollständig), aber ich werde gehen und testen Sie es auf v1. Wenn es funktioniert, werde ich alle oben genannten Szenario, um die test-suite, um sicherzustellen, dass es hat Arbeit in v2.
Edit:
ja, es funktioniert gut auf "v1", mit der Ausgabe:
obj.GetType().GetHashCode()
eine schlechte Idee für die Generierungfield
zahlen, wenn ich vermeiden wollte, ein Magisches vordefinierten Wörterbuch?>= 1
(das ist leicht zu beheben), aber wichtiger ist: die hash-codes sollten nicht wirklich vertrauenswürdig eingestuft werden außerhalb einer bestimmten Anwendungsdomäne. Sie kann sich ändern, zum Beispiel der string-hash-Algorithmus geändert zwischen 1.1 und 2.0 - und legitim könnte sich auch wieder ändern. Es wäre besser, etwas zu verwenden, wie ein MD5-hash der vollständigen Typnamen. Oder: machen Sie Ihre erste Nachricht (mit Bereich 1 oder ähnliches) der Satz von Zuordnungen zwischen zahlen und Typ-Namen.600542181
kommt wieder als63671269
.29723
funktioniert, obwohl. Was sind die Grenzen auf dem Feld? Ich lasse Sie wissen, wenn ich weitere Probleme oder Informationen.Habe ich eine andere Lösung, aber ich entschied mich, es als Antwort, nicht die Frage, denn das macht mehr Sinn für mich. Es ist ziemlich hässlich, meiner Meinung nach, und ich habe davor gewarnt, gegen die Reflexion, also bitte kommentieren oder bessere Antworten, wenn Sie Sie haben. Danke!