boost async_accept не работает с опцией boost asio use_future - PullRequest
0 голосов
/ 16 июня 2019

Я хочу слушать boost::asio::ip::tcp::socket с таймаутом. Для этого я использую функцию std::future::wait_for. Ниже мой код:

std::optional<boost::asio::ip::tcp::socket> server::listen()
{
    boost::asio::ip::tcp::socket sock(io_service);
    std::future<void> accept_status = acceptor.async_accept(
        sock, boost::asio::use_future);
    if (accept_status.wait_for(std::chrono::seconds(10)) == std::future_status::timeout)
    {
        // I hope there's no race-condition between
        // accepting a connection and calling cancel
        acceptor.cancel();
        std::cerr << "Timeout" << std::endl;
        return {};
    }
    std::cerr << "Accepted a connection" << std::endl;
    return {std::move(sock)};
}

Это не работает, хотя: клиент может подключиться, но я все еще получаю тайм-аут. Это означает, что будущий объект и асинхронная функция принятия не взаимодействуют. Чего мне не хватает?

Я использую версию Boost 1.65.

Для Explorer_N приведена полная программа, которая работает не так, как я ожидаю:

#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>
#include <chrono>
#include <future>
#include <iostream>
#include <thread>

using namespace std;

void server_listen() {
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 31132);
    boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
    boost::asio::ip::tcp::socket socket(io_service);
    std::future<void> accept_status = acceptor.async_accept(
            socket, boost::asio::use_future);
    while(true) {
        if(accept_status.wait_for(std::chrono::seconds(10)) == std::future_status::timeout) {
            acceptor.cancel();
            std::cerr << "Timeout\n";
        } else {
            break;
        }
    }
    // if I replace the lines starting from the async_accept call
    // by just the following, everything works as expected
    // acceptor.accept(socket);
    std::cout << "Accepted a connection\n";
    while(true) {
    }
}

void client_connect() {
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::socket socket(io_service);
    boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve({"127.0.0.1", std::to_string(31132)}));
    socket.connect(endpoint);
    std::cout << "Connected to server\n";
    while(true) {
    }
}

int main() {
    std::thread server(server_listen);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::thread client(client_connect);
    while(true) {
    }
}

Составлено g++ -std=c++17 <program>.cpp -lpthread -lboost_system -o <program>.

Вывод, который я получаю:

Connected to server
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
Timeout
...

1 Ответ

1 голос
/ 24 июня 2019

Чтобы ответить на ваши претензии:

"Будущий объект и асинхронная функция принятия не взаимодействуют" - невозможно.

"клиент может подключиться, но я все еще получаютайм-аут. ", - ваш клиент, подключающийся к слушателю, является одним событием, а выполнение обработчика завершения (установка обещания) - другим событием.

Таким образом, соединение могло быть принято на 9-й секунде, и обратный вызов был бы запланированбежать на 11-й секунде (например).

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

за исключением формы, которая

// I hope there's no race-condition between
        // accepting a connection and calling cancel
        acceptor.cancel();
        std::cerr << "Timeout" << std::endl;
        return {};

acceptor.cancel(); просто соберите ожидающих официантов и заполните их ec, установленным на operation_aborted, если обработчики уже вышли в очередь событий завершения, то метод cancel () не работает

Расширение моего ответа на основев недавнем редактировании OP:

using namespace std;

void server_listen() {
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 31132);
    boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
    boost::asio::ip::tcp::socket socket(io_service);
    auto work = make_work_guard(io_service);
    using type= std::decay_t<decltype(work)>;
    std::thread io([&](){io_service.run();});
    std::future<void> accept_status = acceptor.async_accept(
            socket, boost::asio::use_future);
    if(accept_status.wait_for(std::chrono::seconds(10)) == std::future_status::timeout) {
        acceptor.cancel();
        std::cerr << "Timeout\n";
        work.~type();
        //break;
    } else {
        std::cout<<"future is ready\n";
        work.~type();
       // break;
    }

    io.join();
    // if I replace the lines starting from the async_accept call
    // by just the following, everything works as expected
    // acceptor.accept(socket);
    std::cout << "Accepted a connection\n";

}

void client_connect() {
    boost::asio::io_service io_service;
    boost::asio::ip::tcp::resolver resolver(io_service);
    boost::asio::ip::tcp::socket socket(io_service);
    boost::asio::ip::tcp::endpoint endpoint(*resolver.resolve({"127.0.0.1", std::to_string(31132)}));
    socket.connect(endpoint);
    std::cout << "Connected to server\n";

}
enter code here
int main() {
    std::thread server(server_listen);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::thread client(client_connect);
    server.join(); client.join();
}

В вашей программе есть много вещей, о которых нужно позаботиться (избегайте ненужных спин-циклов, не забывайте присоединяться или отключать std::thread и убедитесь, что вы звонитеio_service::run при использовании async* версии)

Start
Connected to server
future is ready
Accepted a connection
0
Finish
...