There is the following class:

[Serializable] public class Packet : ISerializable { private byte[] _userId; private byte[] _userMessage; public Packet() { } public Packet(byte[] userId, byte[] userMessage) { UserMessage.CopyFrom(ref _userMessage, userMessage); UserId.CopyFrom(ref _userId, userId); } protected Packet(SerializationInfo info, StreamingContext context) { UserId = info.GetValue(nameof(_userId), typeof(byte[])).To<byte[]>(); UserMessage = info.GetValue(nameof(_userMessage), typeof(byte[])).To<byte[]>(); } public byte[] UserId { get => _userId; set => _userId = value; } public byte[] UserMessage { get => _userMessage; set => _userMessage = value; } public byte[] ByteData { get { byte[] dataBytes; using (MemoryStream stream = new MemoryStream()) { BinaryFormatter fmt = new BinaryFormatter(); fmt.Serialize(stream, this); dataBytes = new byte[stream.GetBuffer().Length]; dataBytes.CopyFrom(stream.GetBuffer()); } return dataBytes; } } public virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue(nameof(_userId), _userId, typeof(byte[])); info.AddValue(nameof(_userMessage), _userMessage, typeof(byte[])); } } 

And a small test to check the class:

 byte[] userId = new byte[256]; for (int i = 0; i < 256; i++) { userId[i] = (byte)i; } Packet packet = new Packet(userId, userId); byte[] data = packet.ByteData; Packet endDataPacket = new Packet(); using (MemoryStream stream = new MemoryStream()) { BinaryFormatter fmt = new BinaryFormatter(); stream.Write(data, 0, data.Length); endDataPacket = fmt.Deserialize(stream).To<Packet>(); } 

And a small class of extensions:

 public static class Extensions { public static T To<T>(this object self) { return (T) self; } public static void CopyFrom(this byte[] source, byte[] from) { Array.Copy(from, source, from.Length); } public static void CopyFrom(this byte[] self, ref byte[] source, byte[] from) { if (source == null) source = new byte[from.Length]; source.CopyFrom(from); } //public static T2 ArrayRemap<T, T2>(this T self, T2 destType) where T2 : IEnumerable<T2> where T : IEnumerable<T> //{ // T2[] tmpData = new T2[self.To<IEnumerable<T>>().Count()]; //} } 

Next, when I serialize an object, everything is fine, but when I try to deserialize it, I get the following:

An exception was thrown: "System.Runtime.Serialization.SerializationException" in mscorlib.dll An exception like "System.Runtime.Serialization.SerializationException" occurred in mscorlib.dll, but was not processed in the user code. The end of the stream was detected before the parsing was completed.

What could be the reason for this behavior?

  • one
    and after writing to the stream and before deserialization, the pointer does not need to be set in the beginning? - Anatol

1 answer 1

As already managed to say @Anatol in the comments, the error is caused by the fact that you did not move the pointer to the beginning of the stream and reading during deserialization starts from its end

To fix this, it is enough to set the Stream.Position property to 0 (since the beginning of the recording of the serialized object equals the beginning of the stream itself)
I.e:

 using (MemoryStream stream = new MemoryStream()) { BinaryFormatter fmt = new BinaryFormatter(); stream.Write(data, 0, data.Length); stream.Position = 0; // Ставим указатель на начало endDataPacket = fmt.Deserialize(stream).To<Packet>(); // Успешная десериализация } 

Not really related to the question, but I want to note one thing:

As I understand it, you are creating some kind of bridge between two services that can receive / send some type of packets.

Using System.Runtime.Serialization is not good for this.
Look: you store 512 bytes of information, and during serialization you get 1024 ( data.Length ), which is 2 times as much (there is no direct correlation like 1 to 2 , but the serialization used by you always produces rather cumbersome (relative to real) data arrays)

In case you can completely abandon the reference data types within your structure, I can offer you the following option:

 // Пример пакета public unsafe struct Packet { // System.Int32 и без того является типом значения public int userID; // Для примера сделал фиксированное поле public fixed byte userMessage[256]; public Packet(int userID, byte[] userMessage) { this.userID = userID; fixed (byte* id = this.userMessage) for (int i = 0; i < 255; ++i) id[i] = userMessage[i]; } } 

Demonstration extension:

 public static class StructExtensions { public static byte[] Zip<T>(this T Obj) where T : struct { // Получим размер, занимаемый объектом int size = Marshal.SizeOf(Obj); // Сюда будем читать данные byte[] bytes = new byte[size]; // Выделим память и получим указатель на выделенный блок IntPtr ptr = Marshal.AllocHGlobal(size); try { // Пишем данные структуры в неуправляемый блок памяти Marshal.StructureToPtr(Obj, ptr, false); // Копируем данные Marshal.Copy(ptr, bytes, 0, size); } catch (Exception exception) { // Даже в случае ошибки память следует освободить Marshal.FreeHGlobal(ptr); throw exception; } Marshal.FreeHGlobal(ptr); return bytes; } // Не надо создавать расширения для стандартных типов // Здесь я это делаю чисто ради наглядности примера public static T Unzip<T>(this byte[] Bytes) where T : struct { // Получаем дексриптор (указываем, что он закреплен, чтобы сборщик мусора не баловался) GCHandle handle = GCHandle.Alloc(Bytes, GCHandleType.Pinned); try { // Читаем структуру из блока памяти T theStructure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); handle.Free(); // Возвращаем ее return theStructure; } catch (Exception exception) { // Даже в случае ошибки память следует освободить handle.Free(); throw exception; } } } 

And the test itself:

 // Подготовим тестовый массив данных byte[] userId = Enumerable.Range(0, 255).Select(x => (byte)x).ToArray(); // Инициализируем объект Packet packet = new Packet(10, userId); // Сериализуем его и тут же десериализуем обратно Packet endDataPacket = packet.Zip().Unzip<Packet>(); // userID == 10, userMessage = [0..255] 

Notice that the .Zip() method will return to us an array of bytes of length 260 (4 for int and 256 for the array), that is, exactly as much as we store

The essence of this method is that we simply copy the section of memory occupied by the structure into an array of bytes. Then we can write this array in the same section of memory allotted for a similar (at least in size) structure
That is why it is important to abandon (within the framework of this method) from reference types, because in their "place" there are pointers to other memory blocks, where their data is stored. Thus, within the same machine, the copying of the structure, which includes reference types (like arrays), will be successful (if the pointers to objects have not changed), but when you transfer such a block to a remote machine, you will get very unexpected results (more precisely , unspecified )

I do not know the specifics of your situation, so do not take my advice for truth
For this is just a tip, which is also not applicable to all situations)

  • You answered 2 of my questions, perhaps without even knowing it :) ru.stackoverflow.com/questions/878436/… - LLENN
  • @LLENN, do you mind if I then transport the second part of the answer to that question? So that in case someone else needs it (and it will need it), they can be easily found - Kir_Antipov
  • Yes, it will be better. - LLENN
  • @LLENN, excellent) Then in the evening I will rewrite an example and answer) - Kir_Antipov