Close and reopen boost asio acceptor

I have been using this example from boost for a single threaded server: https://www.boost.org/doc/libs/1_81_0/doc/html/boost_asio/example/cpp03/http/server/

I modified the server class so that I could start the server, end the server and restart it.

server_impl::server_impl(
    const std::string& address,
    const std::string& port,
    const std::shared_ptr<boost::asio::io_context>& io_context,
    const std::shared_ptr<connection_manager>& connection_manager,
    const std::shared_ptr<request_handler>& request_handler) :
        io_context_(io_context),
        acceptor_(*io_context_),
        connection_manager_(connection_manager),
        new_connection_(),
        request_handler_(request_handler),
        address_(address),
        port_(port)
{
}

void server_impl::start_server()
{
    // Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
    boost::asio::ip::tcp::resolver resolver(*io_context_);
    boost::asio::ip::tcp::endpoint endpoint =
        *resolver.resolve(address_, port_).begin();
    acceptor_.open(endpoint.protocol());
    acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
    acceptor_.bind(endpoint);
    acceptor_.listen();
    start_accept();
}

void server_impl::end_server()
{
    /* The server is stopped by cancelling all outstanding asynchronous
       operations */
    acceptor_.close();
    connection_manager_->stop_all();
}

void server_impl::start_accept()
{
    new_connection_.reset(new connection_impl(
        io_context_,
        connection_manager_,
        request_handler_));
    acceptor_.async_accept(
        new_connection_->socket(),
        boost::bind(
            &server_impl::handle_accept,
            this,
            boost::asio::placeholders::error));
}

void server_impl::handle_accept(const boost::system::error_code& e)
{
    /* Check whether the server was stopped before this completion handler had a
       chance to run. */
    if (!acceptor_.is_open())
    {
        return;
    }

    if (!e)
    {
        connection_manager_->start(new_connection_);
    }

    start_accept();
}

void server_impl::serve_static(const std::string& path)
{
    request_handler_->serve_from_directory(path);
}

You can see I moved the resolving and listening from the constructor to a start_server function. I then moved the contents of handle_stop to end_server.

This works if I just start the server and end it once like this (Using a thread to end after 30 seconds just to test):

int main()
{
    server->serve_static("/path/to/html");
    server->start_server();

    std::thread t([&server](){sleep(30); server->end_server();});

    io_context->run();

    t.join();
}

But if I try to restart the server after it has been stopped, io_context->run(); returns immediately, I thought that it should block as async_accept was called on the acceptor again:

int main()
{
    server->serve_static("/path/to/html");
    server->start_server();

    std::thread t([&server](){sleep(30); server->end_server();});

    io_context->run();

    t.join();

    server->start_server();

    io_context->run();
}

The server works for the first 30 seconds and then stops like expected, but then when started again, run returns immediately and it doesn’t start again.

>Solution :

Per the boost documentation:

Subsequent calls to run(), run_one(), poll() or poll_one() will return immediately unless there is a prior call to restart().

Leave a Reply