Проблема boost :: asio при передаче данных динамического размера в асинхронный обработчик - PullRequest
1 голос
/ 26 апреля 2019

Я обрабатываю пользовательский пакет данных TCP с Boost.Поскольку все операции выполняются асинхронно, для обработки данных должен быть вызван обработчик.Основная проблема в том, что я не знаю, как передать данные обработчику, когда размер не известен во время компиляции?Например, скажем, вы получаете байты заголовка, анализируете их, которые сообщают вам длину тела:

int length = header.body_size();

Мне как-то нужно выделить массив с размером тела и затем вызвать обработчик (которыйявляется членом класса, а не статической функцией) и передает ему данные.Как мне сделать это правильно?

Я пробовал разные вещи, такие как, но всегда заканчивал тем, что получал segfault, или мне приходилось предоставлять фиксированный размер буфера тела, что не то, что я хочу.Попытка, которую я сделал, может быть найдена ниже.

После получения информации заголовка:

char data[header.body_size()];
boost::asio::async_read(_socket, boost::asio::buffer(data, header.body_size()),
                                    boost::bind(&TCPClient::handle_read_body, this, boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred, data));

Обработчик:

void TCPClient::handle_read_body(const boost::system::error_code &error, std::size_t bytes_transferred,
                                 const char *buffer) {

    Logger::log_info("Reading body. Body size: " + std::to_string(bytes_transferred));
}

В этом примере выдается ошибка сегмента.

Как я могу выделить буфер для тела после определения размера?И как тогда я могу вызвать обработчик и передать error_code, bytes_transferred и данные тела?

Пример фрагмента будет очень полезен, так как примеры надстроечного чата, которые делают это, не оченьмне ясно.

Ответы [ 2 ]

2 голосов
/ 26 апреля 2019

char data[header.body_size()]; не является стандартным в C ++ и станет недействительным, как только выйдет из области видимости, в то время как async_read требует, чтобы буфер оставался в живых до вызова обратного вызова завершения. Поэтому вам, вероятно, следует добавить поле к TCPClient, содержащее список буферов данных (вероятно, типа std :: vector), ожидающих получения.

0 голосов
/ 26 апреля 2019

Все, что вам нужно сделать, это создать буфер в кучу вместо стека.Вместо VLA - char [sizeAtRuntime] вы можете использовать std::string или std::vector с std::shared_ptr.С помощью string / vector вы можете установить буфер любого размера, а с помощью shared_ptr вы можете продлить срок его службы.

Версия с bind:

void foo()
{
    std::shared_ptr<std::vector<char>> buf = std::make_shared<std::vector<char>>(); // buf is local
    buf->resize( header.body_size() );
    // ditto with std::string

    boost::asio::async_read(_socket, boost::asio::buffer(*buf),
         boost::bind(&TCPClient::handle_read_body, 
                     this, boost::asio::placeholders::error,
                     boost::asio::placeholders::bytes_transferred, 
                     buf)); // buf is passed by value
}

void handle_read_body(const boost::system::error_code&, 
                      size_t, 
                      std::shared_ptr<std::vector<char>>)
{

}

в приведенном выше примере buf создается в стеке и указывает на вектор в куче, потому что bind принимает свои аргументы по значению, поэтому buf копируется и счетчик ссылок увеличивается - это означает, что ваш буфер все еще существует, когда async_read заканчивается и foo заканчивается.

Вы можете добиться того же поведения с помощью лямбды, тогда buf должен быть зафиксирован значением:

void foo()
{
    std::shared_ptr<std::vector<char>> buf = std::make_shared<std::vector<char>>(); // buf is local
    buf->resize( header.body_size() );
    // ditto with std::string

    boost::asio::async_read(_socket, boost::asio::buffer(*buf),
                                    boost::bind(&TCPClient::handle_read_body, this, boost::asio::placeholders::error,
                                                boost::asio::placeholders::bytes_transferred, buf)); // buf is passed by value

    boost::asio::async_read(_socket, boost::asio::buffer(*buf),
        [buf](const boost::system::error_code& , size_t)
         ^^^ capture buf by value, increates reference counter of shared_ptr
        {

        });
}
...