Рекурсивная несортировка структуры данных дает ошибку «не удается проанализировать неверные данные о формате провода» в Go Lang Protobuf

ОС и версия protobuf

go1.18.1 linux/amd64, github.com/golang/protobuf v1.5.2

Вступление

Я пытаюсь использовать рекурсивные определения прототипов.

.proto файл

message AsyncConsensus {
  int32 sender = 1;
  int32 receiver = 2;
  string unique_id = 3; // to specify the fall back block id to which the vote asyn is for
  int32 type = 4; // 1-propose, 2-vote, 3-timeout, 4-propose-async, 5-vote-async, 6-timeout-internal, 7-consensus-external-request, 8-consensus-external-response, 9-fallback-complete
  string note = 5;
  int32 v = 6 ; // view number
  int32 r = 7;// round number
  message Block {
    string id = 1;
    int32 v = 2 ; // view number
    int32 r = 3;// round number
    Block parent = 4;
    repeated int32 commands = 5;
    int32 level = 6; // for the fallback mode
  }
  Block blockHigh = 8;
  Block blockNew = 9;
  Block blockCommit = 10;
}

Вот как я маршал и немаршал

func (t *AsyncConsensus) Marshal(wire io.Writer) error {
    data, err := proto.Marshal(t)
    if err != nil {
        return err
    }
    lengthWritten := len(data)
    var b [8]byte
    bs := b[:8]
    binary.LittleEndian.PutUint64(bs, uint64(lengthWritten))
    _, err = wire.Write(bs)
    if err != nil {
        return err
    }
    _, err = wire.Write(data)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) Unmarshal(wire io.Reader) error {

    var b [8]byte
    bs := b[:8]
    _, err := io.ReadFull(wire, bs)
    if err != nil {
        return err
    }
    numBytes := binary.LittleEndian.Uint64(bs)
    data := make([]byte, numBytes)
    length, err := io.ReadFull(wire, data)
    if err != nil {
        return err
    }
    err = proto.Unmarshal(data[:length], t)
    if err != nil {
        return err
    }
    return nil
}

func (t *AsyncConsensus) New() Serializable {
    return new(AsyncConsensus)
}

Мой ожидаемый результат

При маршалинге и отправке в тот же процесс через TCP он должен правильно демаршалировать и создавать правильные структуры данных.

Результирующая ошибка

ошибка "cannot parse invalid wire-format data"

Дополнительная информация

Я пробовал использовать нерекурсивные определения .proto, и раньше у меня никогда не было этой проблемы.

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
2
0
65
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Самая глупая ошибка, о которой я могу думать, заключается в том, что wire.Write(bs) не записывает столько байтов, сколько io.ReadFull(wire, bs) читает, поэтому я просто удостоверился, что их возвращаемое значение на самом деле равно 8 в оба случая.

Тогда я не очень хорошо знаю golang/protobuf, но я думаю, что это должно быть в состоянии сделать это. Разве вы не должны создать код перехода, а затем вызывать его? Я не знаю, как это назвать.

Если вы считаете, что на самом деле проблема заключается в реализации protobuf, есть несколько онлайн-декодеров protobuf, которые могут вам помочь. Но иногда они неправильно интерпретируют поток, что может иметь место в случае с рекурсивным шаблоном, так что вы должны быть осторожны. Но, по крайней мере, они не раз помогали мне отлаживать пакет dedis/protobuf.

В крайнем случае вы можете сделать минимальный пример с рекурсивными данными, проверить, работает ли он, а затем медленно добавлять поля, пока он не сломается…

Ответ принят как подходящий

Это не ошибка Protobuff, а вопрос того, как вы marshal и unmarshal создаете структуры protobuff.

В качестве конкретного руководства никогда не используйте одновременно marshal и unmarshal структуры protobuff, так как это может привести к условиям гонки.

В приведенном вами конкретном примере я вижу рекурсивные структуры данных, поэтому даже если вы используете отдельную структуру для каждого вызова marshal и unmarshal, вполне вероятно, что указатели в родительском элементе могут привести к общим указателям.

Используйте метод глубокого копирования, чтобы удалить любую зависимость, чтобы не столкнуться с условиями гонки.

func CloneMyStruct(orig *proto.AsyncConsensus_Block) (*proto.AsyncConsensus_Block, error) {
    origJSON, err := json.Marshal(orig)
    if err != nil {
        return nil, err
    }

    clone := proto.AsyncConsensus_Block{}
    if err = json.Unmarshal(origJSON, &clone); err != nil {
        return nil, err
    }

    return &clone, nil
}

Другие вопросы по теме