Как можно считывать и разбирать protobuf из потока данных на Python?

Как правильно получать протобуф сообщения из сокета, используя python-библиотеку better-proto? У меня возникли два вопроса:
1) Как правильно использовать метод парсинга в апи библиотеки? Синтаксис Message.parse(bytes) понятен, но неясно, сколько байт нужно вычитывать из сокета заранее, так как их количество может изменяться.
2) Можно ли вычитывать и парсить протобуф сообщения, не зная заранее, какой объект они представляют?
  • 5 ноября 2023 г. 17:09
Ответы на вопрос 2
1) Для правильного использования метода парсинга в библиотеке better-proto, вам необходимо вычитывать все данные сообщения из сокета и передавать их в метод парсинга. Если количество байт данных может изменяться, вы можете использовать специфичный для вашего протокола способ обозначения конца сообщения, например, использование специального разделителя.

Пример использования better-proto для парсинга из потока данных:

```python
from betterproto import Message
from betterproto.lib.google.protobuf.internal.decoder import _DecodeVarint

def parse_protobuf_message(socket):
    # Считываем длину сообщения из потока
    varint_buff = bytearray()
    while True:
        one_byte = socket.recv(1)
        if one_byte == b'':
            # Соединение закрыто
            return None
        varint_buff += one_byte
        try:
            msg_len, _ = _DecodeVarint(varint_buff, 0)
            break
        except IndexError:
            pass

    # Считываем данные сообщения из потока
    msg_buff = bytearray()
    while len(msg_buff) < msg_len:
        one_byte = socket.recv(1)
        if one_byte == b'':
            # Соединение закрыто
            return None
        msg_buff += one_byte

    # Парсим протобуф сообщение
    message = Message.parse(msg_buff)
    return message
```

2) Обычно протоколы, использующие protobuf, имеют описание типов сообщений и их структуру в виде `.proto` файлов. Если у вас есть доступ к этим файлам, то вы можете сгенерировать соответствующий Python-код с помощью утилиты `protoc`, которая идет в комплекте с protobuf. 

Сгенерированный код содержит Python-классы для каждого описанного сообщения, и вы можете использовать эти классы для разбора протобуф сообщений, не зная их структуру заранее. Вам просто потребуется загрузить необходимый класс и вызвать метод `.parse()` или `.from_bytes()` для разбора сообщения.

Пример использования сгенерированного кода для разбора протобуф сообщений без знания их структуры:

```python
from generated_code import ExampleMessage

def parse_protobuf_message(socket):
    msg_buff = bytearray()
    while True:
        one_byte = socket.recv(1)
        if one_byte == b'':
            # Соединение закрыто
            return None
        msg_buff += one_byte

        try:
            message = ExampleMessage.parse(msg_buff)
            return message
        except Exception:
            pass
```

Здесь `ExampleMessage` - это класс, сгенерированный из соответствующего прото-файла, который описывает структуру вашего протобуф сообщения.
Автоматически это сделать невозможно, потому что требуется указать размер сообщения или информацию, по которой размер будет определен. Можно, например, указать тип объекта, чтобы определить его размер. Затем можно загрузить минимальное количество данных, чтобы определить тип, и загрузить остальные данные, если размер неверный.
Похожие вопросы