Задать вопрос
@unknown3

OutOfMemoryException в BinaryFormatter на малых объемах, почему возникает?

Есть клиент который принимает данные с сервера. Используется библиотека SuperSocket.
Ну тут даже не в это дело.
Для приема данных создан фильтр.
В него приходят данные с сервера, далее я их разбираю неким образом и дальше десереализую.
Вот здесь и вся проблема. При десереализации иногда возникает исключение OutOfMemoryException, Вычисление функции отключено из-за исключения, вызванного нехваткой памяти, Измерения массива превысили поддерживаемый диапазон.
С сервера для теста просто шлется 100 сериализованных объектов, вес одного объекта 183 байта.
Клиент может получить 3 объекта и выдать ошибку, может получить 6, может 20, короче под настроение. Хотя объекты всегда шлются одинаковые. Ну один раз запустил программу она приняла 5 и выдала ошибку, второй раз уже 3 и ошибка... Загадка крч.
Ошибка именно при вызове метода Deserialize.

В чём может быть причина?
код фильтра клиента, где ошибки

public class RecieveFilterX : IReceiveFilter<DataPackageInfo>{
        public DataPackageInfo Filter(BufferList data, out int rest){
                if (data.Total > 4){
                    byte[] head = new byte[4];
                    Buffer.BlockCopy(data[0].Array, 0, head, 0, 4);
                    int sizeContent = BitConverter.ToInt32(head, 0);
                    if (data.Total >= sizeContent + 4){
                        int needCopiedBytes = sizeContent + 4;
                        using (MemoryStream msTemp = new MemoryStream()){
                            for (int i = 0; i < data.Count && needCopiedBytes > 0; i++){
                                int lenCopy = Math.Min(needCopiedBytes, data[i].Count);
                                msTemp.Write(data[i].Array, data[i].Offset, lenCopy);
                                needCopiedBytes -= lenCopy;
                            }
                            msTemp.Seek(4, SeekOrigin.Begin);
                            try{
                                BinaryFormatter formatter = new BinaryFormatter();
                                Message msg = (Message)formatter.Deserialize(msTemp);//Ошибка возникает тут
                                rest = data.Total - sizeContent - 4;
                                return new DataPackageInfo(){
                                    Data = msg
                                };
                            }
                            catch (OutOfMemoryException ex){
                                //Вычисление функции отключено из-за исключения, вызванного нехваткой памяти
                                //Измерения массива превысили поддерживаемый диапазон
                                throw ex;
                            }
                        }
                    }
                }
                rest = 0;
                return null;
        }

        public void Reset()
        {
        }

        public IReceiveFilter<DataPackageInfo> NextReceiveFilter { get; set; }
        public FilterState State { get; set; }
    }


Кстате на сервере похожий механизм, ну чуть другой. Но на сервер я могу послать 1000000 объектов и всё отлично залетает без ошибок, могу слать объекты весом несколько мегабайт и всё ок. Хотя там таже десериализация, объекты того же класса, почему же на клиенте ошибки??
Рабочий код фильтра сервера, на котором нет ошибок
class DataRecieveFilter : IReceiveFilter<DataRequestInfo> {
        private byte[] reserveBuff;
        public DataRequestInfo Filter(byte[] readBuffer, int offset, int length, bool toBeCopied, out int rest) {
            byte[] localBuffer = new byte[length];
            if (reserveBuff != null && reserveBuff.Length > 0){
                localBuffer = new byte[reserveBuff.Length + length];
                Buffer.BlockCopy(reserveBuff, 0, localBuffer, 0, reserveBuff.Length);
                Buffer.BlockCopy(readBuffer, offset, localBuffer, reserveBuff.Length, length);
                reserveBuff = null;
            }
            else
                Buffer.BlockCopy(readBuffer,offset,localBuffer,0,length);

            if (localBuffer.Length > 4){
                byte[] head = new byte[4];
                Buffer.BlockCopy(localBuffer, 0, head, 0, 4);
                int sizeContent = BitConverter.ToInt32(head, 0);
                if (localBuffer.Length >= sizeContent + 4){
                        BinaryFormatter formatter = new BinaryFormatter();
                        byte[] dataMessage = new byte[sizeContent];
                        Buffer.BlockCopy(localBuffer, 4, dataMessage, 0, sizeContent);
                        using (MemoryStream msTemp = new MemoryStream(dataMessage)){
                            Message msg = (Message) formatter.Deserialize(msTemp);
                            rest = localBuffer.Length - sizeContent - 4;
                            return new DataRequestInfo()
                            {
                                Key = msg.key,
                                message = msg
                            };
                        }
                }
            }
            rest = 0;
            reserveBuff = localBuffer;
            return null;
        }

        public void Reset()
        {
            reserveBuff = null;
        }

        public int LeftBufferSize { get; set; }
        public IReceiveFilter<DataRequestInfo> NextReceiveFilter { get; set; }
        public FilterState State { get; set; }
    }
  • Вопрос задан
  • 142 просмотра
Подписаться 1 Простой 4 комментария
Решения вопроса 1
sarapinit
@sarapinit Куратор тега C#
Точу водой камень
Я же вам писал, как реализовать FixedHeader в клиенте.

internal class PackageInfo : PackageInfo<FrameType, Frame>
{
    public PackageInfo(FrameType key, Frame body) : base(key, body)
    {
    }
}


internal class ReceiveFilter : FixedHeaderReceiveFilter<PackageInfo>
{
    public ReceiveFilter(int headerSize) : base(headerSize)
    {
    }

    public override PackageInfo ResolvePackage(IBufferStream bufferStream)
    {
        //реализация получения пакета из потока
    }
    

    protected override int GetBodyLengthFromHeader(IBufferStream bufferStream, int length)
    {
        var frameLength = bufferStream.ReadInt32(true);
        return frameLength - HeaderSize;
    }
}


var client = new EasyClient<PackageInfo>();
client.Initialize(new ReceiveFilter(4));
client.NoDelay = true;
client.ReceiveBufferSize = 10240;
client.NewPackageReceived += NewPackageReceived;
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы