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.
CodePudding user response:
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()
.