Tôi đang triển khai ứng dụng máy khách-khách và đang xem xét các cách khác nhau để tuần tự hóa và truyền dữ liệu. Tôi bắt đầu làm việc với Xml Serializers, hoạt động khá tốt, nhưng tạo dữ liệu từ từ và tạo các đối tượng lớn, đặc biệt khi chúng cần được gửi qua mạng. Vì vậy, tôi bắt đầu nhìn vào Protobuf, và protobuf-net.Làm cách nào để gửi nhiều loại đối tượng trên Protobuf?
Vấn đề của tôi nằm trong thực tế là protobuf không gửi thông tin loại với nó. Với Xml Serializers, tôi đã có thể xây dựng một wrapper mà sẽ gửi và nhận bất kỳ đối tượng (serializable) khác nhau trên cùng một dòng, kể từ khi đối tượng serialized vào Xml chứa tên kiểu của đối tượng.
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);
...
Tôi đã xem xét một vài giải pháp cho việc này, bao gồm tạo lớp loại "trạng thái" chính, là loại đối tượng duy nhất được gửi qua ổ cắm của tôi. Điều này di chuyển ra khỏi các chức năng tôi đã làm việc với Xml Serializers, mặc dù, vì vậy tôi muốn tránh hướng đó.
Tùy chọn thứ hai sẽ là bọc các đối tượng protobuf trong một số loại trình bao bọc, xác định loại đối tượng. (Trình bao bọc này cũng sẽ bao gồm các thông tin như ID gói và đích.) Có vẻ ngớ ngẩn khi sử dụng protobuf-net để tuần tự hóa một đối tượng, sau đó gắn luồng đó giữa các thẻ Xml, nhưng tôi đã xem xét nó. Có một cách dễ dàng để có được chức năng này ra khỏi protobuf hoặc protobuf-net?
Tôi đã đưa ra giải pháp thứ ba và đăng nó bên dưới, nhưng nếu bạn có giải pháp tốt hơn, hãy đăng nó lên!
Thông tin về giới hạn lĩnh vực lỗi (sử dụngSystem.String
):
Băm:
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));
}
serialization:
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]
ném một KeyNotFoundException
trong khi tìm kiếm 63671269
.
Nếu tôi thay thế ToInt32
bằng ToInt16
trong hàm băm, giá trị trường được đặt thành 29723
và nó hoạt động. Nó cũng hoạt động nếu tôi xác định rõ ràng trường của System.String
đến 1
. Xác định rõ ràng trường là 600542181
có cùng tác dụng như sử dụng hàm băm để xác định nó. Giá trị của chuỗi được tuần tự hóa không thay đổi kết quả.
gia tăng để kiểm tra bộ như đã hứa: http://code.google.com/p/protobuf -net/source/browse/trunk/Ví dụ/MultiTypesWithLengthPrefix.cs –
Xấu hổ với tôi vì đã đánh giá thấp tính toàn diện của protobuf-net! Sử dụng 'obj.GetType(). GetHashCode()' có phải là một ý tưởng tồi khi tạo các số 'trường', nếu tôi muốn tránh một từ điển được xác định trước? – dlras2
@Daniel - miễn là bạn có một số lược đồ để giảm thiểu chống lại cơ hội * mỏng * của xung đột băm ... –