boost :: asio :: buffer: получение размера буфера и предотвращение переполнения буфера?

У меня есть две следующие функции для отправки и получения пакетов.

void send(std::string protocol)
{
char *request=new char[protocol.size()+1];
request[protocol.size()] = 0;
memcpy(request,protocol.c_str(),protocol.size());

request_length = std::strlen(request);
boost::asio::write(s, boost::asio::buffer(request, request_length));
}
void receive()
{
char reply[max_length];
size_t reply_length = boost::asio::read(s, boost::asio::buffer(reply, request_length));
std::cout << "Reply is: ";
std::cout.write(reply, reply_length);
std::cout << "\n";
}

Вопросы относятся к этой части boost::asio::buffer(reply, request_length) где длина запроса — это длина строки, которая была изначально установлена ​​при отправке пакета. Как проверить размер буфера, не зная request_length? Другой вопрос, как предотвратить переполнение буфера?

7

Решение

Чтобы получить размер буфера, boost::asio::buffer_size() Функция может быть использована. Однако, в вашем примере, это, скорее всего, будет бесполезно для вас.

Как объяснено в буфере обзор, Boost.Asio использует буферные классы для представления буферов. Эти классы обеспечивают абстракцию и защищают операции Boost.Asio от переполнения буфера. Хотя результат boost::asio::buffer() передается операциям, метаданные, такие как размер буфера или его базовый тип, не передаются. Кроме того, эти буферы не владеют памятью, поэтому приложения должны гарантировать, что базовая память остается действительной в течение всего времени существования абстракции буфера.

boost::asio::buffer() Функция предоставляет удобный способ создания буферных классов, где размер буфера определяется из возможного типа. Когда Boost.Asio может определить длину буфера, операции Boost.Asio не будут вызывать переполнение буфера при использовании результирующего типа буфера. Однако, если код приложения указывает размер буфера для boost::asio::buffer()тогда приложения несут ответственность за то, чтобы размер не превышал базовую память.

При чтении данных требуется буфер. Основной вопрос состоит в том, как узнать, сколько памяти выделить, если Boost.Asio не передает размер. Есть несколько решений этой проблемы:

  • Запросите сокет, сколько данных доступно через socket::available(), затем выделите буфер соответственно.

    std::vector<char> data(socket_.available());
    boost::asio::read(socket_, boost::asio::buffer(data));
    
  • Используйте класс, который Boost.Asio может расти в памяти, такой как boost::asio::streambuf. Некоторые операции, такие как boost::asio::read() принимать streambuf объекты в качестве буфера и будут выделять память, необходимую для операции. Тем не менее, условие завершения должно быть предоставлено; в противном случае операция будет продолжаться до тех пор, пока буфер не будет заполнен.

    boost::asio::streambuf data;
    boost::asio::read(socket_, data,
    boost::asio::transfer_at_least(socket_.available()));
    
  • Как Öö Tiib предлагает включить длину как часть протокола связи. Проверьте Boost.Asio Примеры для примеров протоколов связи. Фокус на протоколе, не обязательно на Boost.Asio API.

    • В протоколе фиксированного размера и производитель данных, и потребитель используют одно и то же сообщение размера. Поскольку читатель знает размер сообщения, он может заранее выделить буфер.
    • В протоколе переменной длины сообщения часто делятся на две части: заголовок и тело. Заголовок обычно имеет фиксированный размер и может содержать различную метаинформацию, например длину тела. Это позволяет читателю прочитать заголовок в буфер фиксированного размера, извлечь длину тела, выделить буфер для тела, а затем прочитать тело.

      // Read fixed header.
      std::vector<char> data(fixed_header_size);
      boost::asio::read(socket_, boost::asio::buffer(data));
      
      protocol::header header(data);
      network_to_local(header); // Handle endianess.
      
      // Read body.
      data.resize(header.body_length());
      boost::asio::read(socket_, boost::asio::buffer(data));
      
      protocol::body body(data);
      network_to_local(body); // Handle endianess.
      
15

Другие решения

Обычно протокол связи использует либо сообщения фиксированной длины, либо сообщения, содержащие заголовок, который сообщает длину сообщения.

Boost.Asio онлайн документация содержит большой набор примеров и учебных пособий, так что, возможно, вам стоит начать с этого. Википедия — хороший источник для объяснения передача данных терминология, документация boost asio этого не делает.

5

Я думаю, что ваш вопрос сбивает с толку, но это может помочь:

void receive() {
enum { max_length = 1024 };
char reply[max_length];
size_t reply_length;
std::cout << "Reply is: ";
while ( (reply_length = ba::read(s, basio::buffer(reply, max_length))) > 0) {
std::cout.write(reply, reply_length);
}
std::cout << "\n";
}
1