Запись разделов данных в сокет TCP с boost :: asio :: ip :: tcp из streambuf

0

Примечание для тех, кто голосует: этот вопрос касается не асинхронных аспектов asio (хотя, возможно, здесь может иметь смысл асинхронное решение, что я и предлагаю в конце). На самом деле это просто использование streambuf и ostream с оболочкой asio tcp socket. Примеры/учебник не охватывают этот конкретный аспект (разделение вызова на запись).

Я пишу некоторый (надеюсь, простой) код для среды плагина, которому необходимо отправить довольно большой фрагмент данных (~ 2 МБ) на внешний сервер в ответ на какое-то событие. Данные нужно отправлять достаточно быстро и полностью, но это нечасто, и я не слишком беспокоюсь о производительности. Я использую буферы протокола Google для сериализации данных.

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

#include <boost/asio.hpp>

// connect to the server:
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(server_address, server_port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);

// float_array consists of ~500,000 floats in a ProtoBuf message:
//
// message FloatArray {
//   repeated float data = 1 [packed=true];
// }

// send the serialized float_array to the server:
boost::asio::streambuf b;
std::ostream os(&b);
float_array.SerializeToOstream(&os);
boost::asio::write(socket, b);

// the TCP connection *must* now close to signal the server

Проблема заключается в том, что среда, в которой я работаю (многопоточность), видит операцию write() как слишком длинную (блокирует) и завершает поток. Поскольку мне не разрешено создавать свои собственные потоки, мне нужно разделить операцию write() на несколько отдельных записей.

Моя забота - как это сделать. Я знаю, что могу использовать это для отправки точного количества байтов:

boost::asio::write(socket, b, boost::asio::transfer_exactly(65536));

Но для этого правильно мне нужно точно знать, сколько байтов осталось в Ostream. Я замечаю, что b.size() соответственно сокращается, поэтому я могу использовать это. Однако, чтобы разделить мои записи, мне нужно сохранить некоторое состояние между вызовами этой функции.

Один из вариантов - хранить как streambuf b, так и ostream os между вызовами для моей функции записи, но мне интересно, есть ли лучший способ сделать это. Насколько я могу судить, невозможно частично сериализовать выход ProtoBuf, поэтому я думаю, что я застрял в одном вызове float_array.SerializeToOstream(). Вопрос в том, есть ли надлежащий способ напрямую запросить ostream для количества доступных байтов или, возможно, использовать какой-либо другой механизм (boost::asio::buffer возможно?).

Я с удовольствием рассмотрю документацию boost :: asio, я просто ищу небольшое руководство по продолжению работы, так как там есть много документации для работы, и я не уверен, какие части головоломки имеют значение.

Мысль - возможно ли использовать boost :: asio для создания своего рода однопотокового асинхронного "отправителя" по этим строкам, который может обрабатывать такое состояние для меня? Например, могу ли я вызвать какую-то неблокирующую функцию write() а затем иметь какой-то обратный вызов (или часто посещаемую функцию), который проверяет завершение и затем закрывает TCP-соединение?

  • 0
    Почему за это проголосовали? Некоторая конструктивная обратная связь была бы полезна, а не молчаливым и трусливым понижением. Не ценится
  • 1
    Я полагаю, ваш вопрос был отклонен, потому что кажется, что вы не приложили усилий, чтобы изучить основы Asio, прежде чем задавать этот вопрос. Цель Asio - предоставить простой и элегантный способ выполнения асинхронного ввода-вывода . Вам не нужно читать всю документацию сразу, но, по крайней мере, стоит пройти курс обучения .
Показать ещё 2 комментария
Теги:
protocol-buffers
tcp
boost-asio
iostream

1 ответ

2
Лучший ответ

Хотя я не смог найти способ запросить ostream для определения количества "ожидания" данных в потоке напрямую, я смог полностью исключить ostream и сериализовать данные в массив char*. Это можно затем отправить аналогично методу сокета старого сокета с функцией boost::asio::write():

...
tcp::socket socket(io_service);
char * buffer = new char[size];  // or a smart-ptr
float_array.SerializeToArray(static_cast<void*>(buffer, size));
void * p = static_cast<void*>(buffer);
int bytes_sent = boost::asio::write(socket, boost::asio::buffer(p, bytes_to_send);

В качестве альтернативы, если использование boost::asio::streambuf и std::ostream являются предпочтительными, то оказывается, что streambuf может быть запрошен (с .size()) после того, как ostream был использован для записи некоторых данных:

boost::asio::streambuf b;
std::ostream os(&b);
float_array.SerializeToOstream(&os);

// send a chunk of a particular size
int bytes_to_send = std::min(chunk_size, b.size());
cout << b.size() << endl;  // shows amount of remaining data
boost::asio::write(socket, b, boost::asio::transfer_exactly(bytes_to_send));
cout << b.size() << endl;  // shows a reduction in amount of remaining data

Поэтому, если это вызывается несколько раз (для каждого фрагмента), то необходимо обновить ostream, streambuf и io_service.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню