#c #boost-asio
#c #boost-asio
Вопрос:
У меня есть это требование, когда мое приложение должно подключаться к другому приложению через сокеты и должно поддерживать постоянное соединение в течение длительного времени. Мое приложение будет TCP-клиентом, а другое — TCP-сервером. Мое приложение будет отправлять команды, и сервер ответит соответствующим образом.
Проблема, с которой я сейчас сталкиваюсь, заключается в том, как прочитать все данные с сервера в виде строки и вернуть для приложения, которое выдаст следующую команду. Чтение синхронно (с asio::read
) выглядело хорошим вариантом до тех пор, пока я не заметил, что сокет зависает, пока я не завершу работу сервера. Просматривая документацию, я обнаружил, что библиотека работает правильно.
его функция используется для чтения определенного количества байтов данных из потока. Вызов будет блокироваться до тех пор, пока не будет выполнено одно из следующих условий: 1. Предоставленные буферы заполнены. То есть передаваемые байты равны сумме размеров буфера. 2. Произошла ошибка.
Проблема в том, что я не знаю правильного размера буфера, поскольку ответ от сервера меняется. Поэтому, если я помещаю слишком маленький буфер, он возвращает нормально, но некоторые данные отсутствуют. Если я установлю слишком большой, он будет зависать вечно, пока сервер не завершит работу.
Итак, я подумал, что буду выполнять асинхронное чтение. Это работает только один раз, и я не знаю, как заставить его извлекать данные до тех пор, пока не будут прочитаны все данные.
вот соответствующий асинхронный код
#define ASIO_STANDALONE 1
#include <iostream>
#include <asio.hpp>
int main()
{
asio::io_context context;
size_t reply_length;
size_t length = 1024;
std::vector<char> buffer;
//create socket
asio::ip::tcp::socket socket(context);
socket.connect(asio::ip::tcp::endpoint(asio::ip::address::from_string("127.0.0.1"), 8088));
std::string dataOut = "list --files"; //some command to write
std::error_code error;
asio::write(socket, asio::buffer(dataOut), error);
if (!error)
{
std::cout << "Receiving...!" << std::endl;
buffer.resize(length);
asio::async_read(socket, asio::buffer(buffer), [amp;buffer, amp;context](const asio::error_code amp;ec, std::size_t bytes_transferred) {
std::copy(buffer.begin(), buffer.end(), std::ostream_iterator<char>(std::cout, ""));
std::cout << "nRead total of:" << bytes_transferred << "n";
context.run();
});
}
else
{
std::cout << "send failed: " << error.message() << std::endl;
}
context.run();
}
Поиск не сильно помог решить мою проблему.
Итак, мой вопрос в том, как я могу прочитать все данные в постоянном сокете с помощью asio? Я не использую boost.
Ответ №1:
Вам нужно выполнять циклические async_read
вызовы. Если вы не хотите, чтобы ваш клиент зависал при операции чтения, вы можете определить наименьший возможный буфер, т.е. 1
байт.
Определите функцию, которая принимает сокет, буфер и два дополнительных параметра в соответствии с сигнатурой обработчика async_read, и эта функция вызывает себя с async_read
, чтобы выполнить цикл async_read
вызовов — она считывает, пока не возникнет какая-либо ошибка:
void onRead (
asio::ip::tcp::socketamp; socket,
std::array<char,1>amp; buf,
const system::error_codeamp; ec,
std::size_t bytes)
{
if (ec)
{
if (ec == asio::error::eof amp;amp; bytes == 1)
std::cout << buf[0];
return;
}
std::cout << buf[0];
asio::async_read(socket,asio::buffer(buf),
std::bind(onRead, std::ref(socket), std::ref(buf),
std::placeholders::_1, // error code
std::placeholders::_2)); // transferred bytes
}
и изменения в main
:
std::array<char,1> buf;
asio::write(socket, asio::buffer(dataOut), error);
if (!error)
{
std::cout << "Receiving...!" << std::endl;
asio::async_read(socket, asio::buffer(buf),
std::bind(onRead, std::ref(socket), std::ref(buf),
std::placeholders::_1,
std::placeholders::_2));
context.run();
}
else
{
std::cout << "send failed: " << error.message() << std::endl;
}
(Я использую Boost, поэтому вам следует заменить system::error_code
on asio::error_code
).
Комментарии:
1. Отличное объяснение и код. позвольте мне поиграть, чтобы убедиться, что я все понимаю. Я вернусь!
2. @StefanoMtangoo нет проблем, рад помочь 🙂