Asio двусторонняя связь с постоянным разъемом - PullRequest
0 голосов
/ 11 апреля 2019

У меня есть это требование, когда мое приложение должно подключаться к другому приложению через сокеты и должно поддерживать постоянное соединение в течение долгого времени. Мое приложение будет 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), [&buffer, &context](const asio::error_code &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? Не использую повышение.

1 Ответ

1 голос
/ 11 апреля 2019

Вам нужно зациклить async_read звонки. Если вы не хотите, чтобы ваш клиент зависал при операции чтения, вы можете определить наименьший возможный буфер, т.е. 1 байт.

Определите функцию, которая принимает сокет , буфер и два дополнительных параметра в соответствии с сигнатурой обработчика async_read, и эта функция вызывает себя с помощью async_read, чтобы сделать цикл вызовов async_read - он читает, пока не произойдет какая-либо ошибка:

void onRead (
    asio::ip::tcp::socket&    socket,
    std::array<char,1>&       buf,
    const system::error_code& ec, 
    std::size_t               bytes)
{
    if (ec)
    {
        if (ec == asio::error::eof && 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 на asio::error_code).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...