#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());
Несколько вещей, которые я здесь не понимаю:
- Мы закончили чтение заголовка. Почему нам нужен boost beast, а не boost asio для чтения необработанного tcp-сообщения? Когда я попытался это сделать (как с async_read / async_read_some) Я получил бесконечное чтение нулевого размера.
- В документации parser говорится (в конце страницы), что для каждого сообщения требуется новый экземпляр, но я не вижу этого в примере.
- Поскольку чтение 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<<
tostd::stringstream
.3. Спасибо, Винни. Думаю, теперь я понимаю, как это работает. Мне не нужен сериализатор. Я неправильно понял документацию. Сериализатор не нужен, потому что я не хочу отправлять в сокет. Мне просто нужен был анализатор. Как только я использовал a
string_body
в параметре шаблона синтаксического анализатора, все стало легко, и все работало нормально. Единственная оставшаяся проблема заключается в том, что, поскольку он разбит на фрагменты, а steam никогда не заканчивается, содержимое синтаксического анализатора продолжает расти бесконечно. Есть ли способ очистить / использовать его строковое содержимое? (кроме воссоздания соединения)4. Единственная понятная функция, которую я нашел, предназначена для отдельных объектов body, а не для всего в анализаторе. Проблема в том, что по какой-то причине
body()
все терминаторыrn
in не существовали. Я смог обойти эту проблему, используяpayload_size()
, где я читаю тело, анализирую его с помощью json, если возможно, а затем очищаю строку body() . Понятия не имею, почему терминаторы исчезают. Я могу смириться с этим до тех пор, пока обратные вызовы сообщений / чтения не будут объединены, иначе это приведет к саботажу синтаксического анализа json. Если я делаю что-то из этого неправильно, пожалуйста, дайте мне знать. Лучшие.5. Вы не увидите разделителей фрагментированной кодировки, потому что анализатор Beast удаляет их перед добавлением данных в строку. Как я уже говорил, используйте
buffer_body
, если вы хотите постепенно анализировать бесконечный ответ.