protobuf: прочитать сообщение на C ++ из C #

Я собираюсь прочитать сообщения, которые последовательно хранятся в сокете клиента C ++ и отправляются с сервера C #. Я ожидаю, что смогу прочитать размер такого сообщения:

google::protobuf::uint32 m;
coded_input->ReadVarint32(&m);
cout << m << endl;

Затем я хочу прочитать сообщение:

Person person;
CodedInputStream::Limit limit = coded_input->PushLimit(m);
person.ParseFromCodedStream(coded_input);
coded_input->PopLimit(limit);

код C # выглядит так:

var person = new Person { Id = 123456, Name = "Fred", Address = new Address { Line1 = "Flat 1", Line2 = "The Meadows ar garą " } };
Stream str = new NetworkStream(socket);
Serializer.SerializeWithLengthPrefix(str, person, PrefixStyle.Fixed32);

Не работает.

Я получаю сообщение об ошибке C ++ (43 - результат cout ‹< m ‹< endl;) (id, name, address - все поля в Person)

43

libprotobuf ОШИБКА google / protobuf / message_lite.cc: 123] Невозможно проанализировать сообщение типа "Человек", так как в нем отсутствуют обязательные поля: идентификатор, имя, адрес

Когда я не читаю вариант и не разбираю сообщение прямо из coded_input, все в порядке (когда я изменяю SerializeWithLengthPrefix на сериализацию в коде сервера). Однако мне нужен способ различать последовательные сообщения, поэтому мне нужно знать размер сообщения, которое я собираюсь прочитать. Я просто не знаю, как отправить размер.

Я пытался:

Stream buf = new MemoryStream();
Serializer.Serialize(buf, person);
ProtoWriter writer = new ProtoWriter(str, null);
ProtoWriter.WriteInt32((int)buf.Length, writer);

но потом я получаю:

Необработанное исключение: ProtoBuf.ProtoException: недопустимая операция сериализации с проводным типом Нет в позиции 0 в ProtoBuf.ProtoWriter.WriteInt32 (значение Int32, модуль записи ProtoBuf.ProtoWriter) [0x00000] в: 0
в protobuftest.MainClass.Main (System .String [] args) [0x00097] в /home/lorddidger/studia/csharp/protobuf-test/protobuf-test/Main.cs:31

Я не могу отправить таким образом int. Что случилось?


Обновление: на самом деле моя цель - найти способ передать целое число (размер) с помощью protobuf. Мне нужен какой-либо пример (как C ++, так и C #), как с этим справиться, потому что я не понимаю всех деталей в protobuf.

Я заметил, что SerializeWithLengthPrefix отправляет префикс (4 uint32), который выглядит так: size 0 0 0. Размер - это количество байтов сообщения после сериализации (я думаю). Я думал, что PrefixStyle.Fixed32 говорит, что перед сообщением только один uint32, но их четыре!

Наконец, я подумал, что вам следует использовать ProtoWriter.WriteInt32 ((int) buf.Length, writer) для передачи целых чисел, потому что я следовал совету где-то в Интернете, что, как я полагаю, является обходным путем, относящимся к C ++. Теперь я вижу, что вам не следует писать варинты - они связаны с движком и слишком сложны, чтобы тратить на них время.

Должен ли я отправлять сообщение с int? Как отличить это сообщение от следующего, размер которого хранится в первом?

Я вижу, что C # может нормально обрабатывать префиксы, но есть ли ЛЮБОЙ СОВМЕСТИМЫЙ с C ++ и C # способ указать размер сообщения? В противном случае не существует * * способа поставить сообщения в очередь, что я считаю иррациональным.


person lord.didger    schedule 03.06.2011    source источник


Ответы (1)


Ваш первый пример включает только длину; «с префиксом длины» фактически кодируется в потоке, совместимом с protobuf.

Если вы декодируете из C ++, прочтите две варинты; первая - это номер поля и тип провода; второй - длина. Первый упакован как 3-битный тип провода, остальные - это номер поля. Вы также можете указать поле 0, которое нужно пропустить - я не могу вспомнить без проверки.


Обновлять; Я проверил это, записав данные без номера поля (просто префикс длины varint), а затем прочитав их обратно на C #, используя два разных API - индивидуально с помощью Deserialize и как перечислимый блок с помощью DeserializeItems. Мне пришлось исправить ошибку в последнем, но следующее будет работать после следующего нажатия кода (примечание: исправление есть только в коде чтения, поэтому, если вы пишете на C # и чтение на C ++ на вас это не повлияет):

using (var ms = new MemoryStream())
{
    // write data with a length-prefix but no field number
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 1 }, PrefixStyle.Base128, 0);
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 2 }, PrefixStyle.Base128, 0);
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 3 }, PrefixStyle.Base128, 0);

    ms.Position = 0;
    Assert.AreEqual(9, ms.Length, "3 lengths, 3 headers, 3 values");

    // read the length prefix and use that to limit each call
    TypeModel model = RuntimeTypeModel.Default;
    int len, fieldNumber, bytesRead;
    List<Foo> foos = new List<Foo>();
    do
    {
        len = ProtoReader.ReadLengthPrefix(ms, false, PrefixStyle.Base128, out fieldNumber, out bytesRead);
        if (bytesRead <= 0) continue;

        foos.Add((Foo)model.Deserialize(ms, null, typeof(Foo), len));

        Assert.IsTrue(foos.Count <= 3, "too much data!");
    } while (bytesRead > 0);

    Assert.AreEqual(3, foos.Count);
    Assert.AreEqual(1, foos[0].Bar);
    Assert.AreEqual(2, foos[1].Bar);
    Assert.AreEqual(3, foos[2].Bar);

    // do it using DeserializeItems
    ms.Position = 0;

    foos.Clear();
    foreach (var obj in model.DeserializeItems<Foo>(ms, PrefixStyle.Base128, 0))
    {
        foos.Add(obj);
        Assert.IsTrue(foos.Count <= 3, "too much data!");
    }
    Assert.AreEqual(3, foos.Count);
    Assert.AreEqual(1, foos[0].Bar);
    Assert.AreEqual(2, foos[1].Bar);
    Assert.AreEqual(3, foos[2].Bar);
}
person Marc Gravell    schedule 03.06.2011
comment
Ооо, Марк, это всего лишь несколько тысяч строк кода ... ты не помнишь? ;) - person IAbstract; 03.06.2011
comment
@IAbstract, как мне хотелось бы, чтобы их было всего несколько тысяч :) добавить в режиме выполнения, полный компилятор IL, реализацию v1, различные версии платформы (CF, SL, WP7, Mono и т. Д.) ), исходный набор тестов, набор тестов метамодели v2, генератор кода (.proto), инструменты VS, ... Скажем так, складывается :) - person Marc Gravell; 04.06.2011
comment
На этот раз я немного расслаблюсь ... :) - person IAbstract; 04.06.2011