Как читать данные, когда длина неизвестна? - PullRequest
0 голосов
/ 06 февраля 2019

В настоящее время я использую boost :: asio для чтения данных через сетевые подключения, но остановился на шаблоне, который я считаю неэффективным:

auto listener::read(std::function<void(std::error_code ec, packet packet)> callback) noexcept -> void {
  m_buffer.resize(1);
  m_buffer.shrink_to_fit();

  asio::async_read(*m_socket, asio::buffer(m_buffer), asio::transfer_exactly(1),
                   [&, callback](std::error_code ec, std::size_t length) {
                     const auto available = m_socket->available();
                     packet tmp;
                     tmp.resize(available);
                     asio::async_read(*m_socket, asio::buffer(tmp), asio::transfer_exactly(available));
                     tmp.insert(tmp.begin(), std::make_move_iterator(m_buffer.begin()),
                                std::make_move_iterator(m_buffer.end()));
                     callback(ec, std::move(tmp));
                   });
}

(packet - std::vector<unsigned char>)

Я не уверен, как создать это без временного.Я не могу изменить размер m_buffer в начале, потому что я не знаю, сколько данных поступает.Я попытался использовать m_buffer только для изменения размера в лямбде, чтобы соответствовать available + 1, но я просто потерял первый байт, хранящийся в m_buffer.

Есть ли более эффективный способ сделать это, когдаожидаете пакет неизвестной длины?

1 Ответ

0 голосов
/ 06 февраля 2019

Во-первых, вы не можете сделать это:

 asio::async_read(*m_socket, asio::buffer(tmp), asio::transfer_exactly(available)); //[1]
 tmp.insert(tmp.begin(), std::make_move_iterator(m_buffer.begin()),
                            std::make_move_iterator(m_buffer.end())); // [2]

в [1] запускается асинхронная операция.async_read возвращается немедленно.Затем у нас есть две параллельные операции: первая вставляется в tmp, вторая (асинхронная операция) заполняет tmp некоторыми данными.Вы можете использовать синхронную операцию: asio::read вместо asio::async_read, первая - это функция блокировки, поэтому insert выполняется только при чтении данных.


Если вы не хотитеиграть с объединяющимся вектором, создавать временные объекты и т. д. вы можете использовать boost :: asio :: dynamic_buffer :

struct listener {
 vector<char> m_buffer;
 // others members
};

void listener::read(std::function<void(std::error_code ec, packet p)> callback) 
{
  boost::asio::async_read(m_socket, boost::asio::dynamic_buffer(m_buffer), boost::asio::transfer_exactly(1),
                                                 ^^^^^^^^^^^^^
             [&, callback](std::error_code ec, std::size_t length) 
             {
                 const auto available = m_socket.available();
                 boost::asio::async_read(m_socket, boost::asio::dynamic_buffer(m_buffer), 
                                                                ^^^^^^^^^^^^^
                      boost::asio::transfer_exactly(available),
                   [this,callback](const boost::system::error_code& ec, size_t)
                   {
                      callback(ec, std::move(m_buffer));
                   });
            });
}

m_buffer автоматически увеличивается на асинхронная операция .Вы не делаете это вручную.Как видите, я добавил новый обработчик -> где он называется callback(ec,move(m_buffer)).Когда вызывается этот обработчик, мы знаем, что операция чтения заканчивается.

...