Как я могу преобразовать сериализованные данные в boost :: beast в строку, чтобы я мог обрабатывать их в формате FIFO?

#c #http #boost #boost-asio #boost-beast

#c #http #boost #boost-asio #boost-beast

Вопрос:

У меня есть клиентское приложение, где мне нужно получать http «длительные запросы» с сервера. Я отправляю команду, и после получения заголовка ответа мне нужно просто получать данные json, разделенные rn символом, пока соединение не будет прервано.

Мне удалось адаптировать пример клиента boost beast для отправки сообщения и получения заголовка, его анализа и получения ответов от сервера. Однако мне не удалось найти способ сериализовать данные, чтобы я мог обрабатывать сообщения json.

Ближайшую демонстрацию проблемы можно найти в этом примере relay . В этом примере ( p это анализатор, sr сериализатор, input входной поток сокета и output выходной поток сокета) после чтения заголовка http у нас есть цикл, который непрерывно считывается с сервера:

 do
{
    if(! p.is_done())
    {
        // Set up the body for writing into our small buffer
        p.get().body().data = buf;
        p.get().body().size = sizeof(buf);

        // Read as much as we can
        read(input, buffer, p, ec);

        // This error is returned when buffer_body uses up the buffer
        if(ec == error::need_buffer)
            ec = {};
        if(ec)
            return;

        // Set up the body for reading.
        // This is how much was parsed:
        p.get().body().size = sizeof(buf) - p.get().body().size;
        p.get().body().data = buf;
        p.get().body().more = ! p.is_done();
    }
    else
    {
        p.get().body().data = nullptr;
        p.get().body().size = 0;
    }

    // Write everything in the buffer (which might be empty)
    write(output, sr, ec);

    // This error is returned when buffer_body uses up the buffer
    if(ec == error::need_buffer)
        ec = {};
    if(ec)
        return;
}
while(! p.is_done() amp;amp; ! sr.is_done());
  

Несколько вещей, которые я здесь не понимаю:

  1. Мы закончили чтение заголовка. Почему нам нужен boost beast, а не boost asio для чтения необработанного tcp-сообщения? Когда я попытался это сделать (как с async_read / async_read_some) Я получил бесконечное чтение нулевого размера.
  2. В документации parser говорится (в конце страницы), что для каждого сообщения требуется новый экземпляр, но я не вижу этого в примере.
  3. Поскольку чтение tcp-сообщений не работает, есть ли способ преобразовать данные анализатора / сериализатора в какую-то строку? Даже записать его в текстовый файл в формате FIFO, чтобы я мог обработать его с помощью какой-нибудь библиотеки json? Я не хочу использовать другой сокет, подобный примеру.

Функции boost::beast::buffers() не удалось скомпилировать для синтаксического анализатора и сериализатора, а для синтаксического анализатора нет функции consume, и, похоже, что функция consume сериализатора предназначена для определенных http-частей сообщения, которые запускают assert, если я делаю это для body() .

Кроме того, мне также не удалось получить согласованные фрагменты данных из анализатора и буфера старой школы std::copy . Кажется, я не понимаю, как объединить данные вместе, чтобы получить поток данных. Использование буфера с .consume() в любой момент при получении данных приводит к need buffer ошибке.

Я был бы очень признателен, если бы кто-нибудь объяснил логику того, как все это должно работать вместе.

Ответ №1:

Где это buf ? Вместо этого вы могли бы читать непосредственно в std::string . Вызовите string.resize(N) и установите указатель и размер в buffer_body::value_type to string.data() и string.size() .

Комментарии:

1. IIUYC, единственное изменение, которое он мог бы сделать, это заменить buf на string . Но тогда у меня проблемы не здесь. У меня возникли проблемы после сериализатора. Вместо того, чтобы сериализатор записывался в сокет (как показано в примере, который я скопировал из http_relay), я бы хотел, чтобы он переходил в строку / представление. Вы уже упоминали о разделении на фрагменты, и это законная проблема. Особенно то, что во всех моих попытках преобразовать что-либо в строку у меня повторяющиеся части данных. Не могли бы вы, пожалуйста, написать небольшой фрагмент кода, который заменяет write(output, sr, ec); что-то, что преобразуется sr в строку?

2. Вы хотите преобразовать HTTP-сообщение в строку? Используйте operator<< to std::stringstream .

3. Спасибо, Винни. Думаю, теперь я понимаю, как это работает. Мне не нужен сериализатор. Я неправильно понял документацию. Сериализатор не нужен, потому что я не хочу отправлять в сокет. Мне просто нужен был анализатор. Как только я использовал a string_body в параметре шаблона синтаксического анализатора, все стало легко, и все работало нормально. Единственная оставшаяся проблема заключается в том, что, поскольку он разбит на фрагменты, а steam никогда не заканчивается, содержимое синтаксического анализатора продолжает расти бесконечно. Есть ли способ очистить / использовать его строковое содержимое? (кроме воссоздания соединения)

4. Единственная понятная функция, которую я нашел, предназначена для отдельных объектов body, а не для всего в анализаторе. Проблема в том, что по какой-то причине body() все терминаторы rn in не существовали. Я смог обойти эту проблему, используя payload_size() , где я читаю тело, анализирую его с помощью json, если возможно, а затем очищаю строку body() . Понятия не имею, почему терминаторы исчезают. Я могу смириться с этим до тех пор, пока обратные вызовы сообщений / чтения не будут объединены, иначе это приведет к саботажу синтаксического анализа json. Если я делаю что-то из этого неправильно, пожалуйста, дайте мне знать. Лучшие.

5. Вы не увидите разделителей фрагментированной кодировки, потому что анализатор Beast удаляет их перед добавлением данных в строку. Как я уже говорил, используйте buffer_body , если вы хотите постепенно анализировать бесконечный ответ.