Как узнать длину байтового массива для десериализации с использованием protobuf-net? - PullRequest
0 голосов
/ 31 января 2019

Я пытаюсь отправить объект C # из одного процесса в другой, используя файл с отображением в память, и я пытаюсь использовать либо BinaryFormatter, либо protobuf-net.Ни один из них не работает - очевидно, потому что я использую по необходимости байтовый массив фиксированной длины, а protobuf-net нужен массив точно правильной длины?

Используя protobuf-net, я получаю это исключение при десериализации:«ProtoException:« Неиспользуемые данные, оставленные в буфере; это предполагает повреждение ввода »в строке:« message1 = Serializer.Deserialize (memoryStream);

Вот мой код.На данный момент я просто пытаюсь привести простой пример, чтобы заставить его работать на базовом уровне: это объект, который я хочу отправить между программами:

[ProtoContract]
public class IpcMessage
{
    public IpcMessage() { }

    [ProtoMember(1)]
    public string title { get; set; }

    [ProtoMember( 2 )]
    public string content { get; set; }
}

Вот (упрощенное -Я удалил код синхронизации), который отправляет объект IpcMessage:

static void SampleSend()
{
    // Create the memory-mapped file which allows 'Reading' and 'Writing'
    using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen( "MyMmfName", 1024, MemoryMappedFileAccess.ReadWrite ))
    {
        // Create a view-stream for this process, which allows us to write data from offset 0 to 1024 (whole memory)
        using (MemoryMappedViewStream mmvStream = mmf.CreateViewStream( 0, 1024))
        {
            IpcMessage message1 = new IpcMessage();
            message1.title = "test";
            message1.content = "hello world";
            Serializer.Serialize( mmvStream, message1 );
        }
    }
}

, а вот код в принимающей программе (немного упрощен):

// Create the memory mapped file..
using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen( "MyMmfName", 1024, MemoryMappedFileAccess.ReadWrite ))
{
    using (MemoryMappedViewAccessor mmvStream = mmf.CreateViewAccessor( 0, 1024, MemoryMappedFileAccess.Read ))
    {
        byte[] buffer = new byte[1024];
        IpcMessage message1;
        int numberBytesRead = mmvStream.ReadArray<byte>( 0, buffer, 0, 1024 );
        var memoryStream = new MemoryStream(buffer);
        // It is at this next line that I get ProtoException: 'Unconsumed data left in the buffer; this suggests corrupt input'
        message1 = Serializer.Deserialize<IpcMessage>( memoryStream );
    }
}

Когда я пыталсяиспользовать BinaryFormatter он тоже жаловался.Очевидно, что я делаю что-то совершенно неправильное.

Просматривая другие вопросы - я вижу, что большинство реализаций, похоже, имеют байтовый массив, который выделен только на правильную длину.Здесь я не знаю длину заранее - это просто массив с фиксированной длиной 1024 байта (я выбрал этот размер произвольно, только сейчас).Или, может быть, это что-то очевидное, что я упускаю?

Я впервые использую файл с отображением в памяти или protobuf-net.Будем благодарны за любую помощь или совет. Заранее спасибо.

Примечание. Я использую Visual Studio 2017 Enterprise 15.9.6, и этот код предназначен для .NET Framework 4.0

.

1 Ответ

0 голосов
/ 01 февраля 2019

Вариант 1: укажите MemoryStream правильное количество байтов для использования в необязательной перегрузке конструктора;это будет работать для всех сериализаторов.

Вариант 2, специально для protobuf-net: use ProtoReader;API Deserialize принимает Stream или a ProtoReader;последний может быть сконструирован с условной длиной и не будет перечитывать

Вариант 3, снова protobuf-net: используйте API *WithLengthPrefix для сериализации и десериализации

Обратите внимание, что любой параметр предполагаетчто ваш код может надежно определить длину, которая обычно должна передаваться отдельно, независимо от того, какой подход вы используете.Вариант 3 имеет дело с этим внутренне, но во многих сценариях ваш собственный код все еще должен учитывать фреймы, чтобы вы не потребляли слишком много и читали I до следующего сообщения (если только это не UDP с ровно одним кадром на пакет, но... это не так, потому что MemoryMappedFile).Вам также необходимо учитывать, что произойдет, если полезная нагрузка будет больше ожидаемого размера буфера.

Простейшим способом "кадрирования" будет резервирование 4 байтов в начале каждого логического блока;сериализуйте, начиная со смещения 4, а затем запишите число байтов, записанных обратно со смещением 0–3, используя фиксированную 4-байтовую разметку в выбранном вами порядке байтов (обычно «маленький»).При чтении: прочитайте первые 4 байта, чтобы получить длину, затем потребьте столько байтов из сегмента.По сути, это то, что *WithLengthPrefix делает внутренне, за исключением того, что поддерживается несколько различных вариантов компоновки.

...