Ответ 1
Короче говоря, есть два варианта:
- Измените код на асинхронный (
acceptor::async_accept()
иasync_read
), запустите в цикле событий черезio_service::run()
и отмените его черезio_service::stop()
. - Сила блокирует вызовы для прерывания с помощью механики более низкого уровня, например сигналов.
Я бы порекомендовал первый вариант, так как он скорее переносится и проще в обслуживании. Важно понимать, что io_service::run()
блокируется только при ожидающей работе. Когда вызывается io_service::stop()
, он попытается вызвать все потоки, заблокированные на io_service::run()
, чтобы вернуться как можно быстрее; он не будет прерывать синхронные операции, такие как acceptor::accept()
и socket::receive()
, даже если синхронные операции вызывают в цикле событий. Важно отметить, что io_service::stop()
является неблокирующим вызовом, поэтому синхронизация с потоками, которые были заблокированы на io_service::run()
, должна использовать другой механизм, такой как thread::join()
.
Вот пример, который будет работать в течение 10 секунд и прослушивает порт 8080:
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <iostream>
void StartAccept( boost::asio::ip::tcp::acceptor& );
void ServerThreadFunc( boost::asio::io_service& io_service )
{
using boost::asio::ip::tcp;
tcp::acceptor acceptor( io_service, tcp::endpoint( tcp::v4(), 8080 ) );
// Add a job to start accepting connections.
StartAccept( acceptor );
// Process event loop.
io_service.run();
std::cout << "Server thread exiting." << std::endl;
}
void HandleAccept( const boost::system::error_code& error,
boost::shared_ptr< boost::asio::ip::tcp::socket > socket,
boost::asio::ip::tcp::acceptor& acceptor )
{
// If there was an error, then do not add any more jobs to the service.
if ( error )
{
std::cout << "Error accepting connection: " << error.message()
<< std::endl;
return;
}
// Otherwise, the socket is good to use.
std::cout << "Doing things with socket..." << std::endl;
// Perform async operations on the socket.
// Done using the socket, so start accepting another connection. This
// will add a job to the service, preventing io_service::run() from
// returning.
std::cout << "Done using socket, ready for another connection."
<< std::endl;
StartAccept( acceptor );
};
void StartAccept( boost::asio::ip::tcp::acceptor& acceptor )
{
using boost::asio::ip::tcp;
boost::shared_ptr< tcp::socket > socket(
new tcp::socket( acceptor.get_io_service() ) );
// Add an accept call to the service. This will prevent io_service::run()
// from returning.
std::cout << "Waiting on connection" << std::endl;
acceptor.async_accept( *socket,
boost::bind( HandleAccept,
boost::asio::placeholders::error,
socket,
boost::ref( acceptor ) ) );
}
int main()
{
using boost::asio::ip::tcp;
// Create io service.
boost::asio::io_service io_service;
// Create server thread that will start accepting connections.
boost::thread server_thread( ServerThreadFunc, boost::ref( io_service ) );
// Sleep for 10 seconds, then shutdown the server.
std::cout << "Stopping service in 10 seconds..." << std::endl;
boost::this_thread::sleep( boost::posix_time::seconds( 10 ) );
std::cout << "Stopping service now!" << std::endl;
// Stopping the io_service is a non-blocking call. The threads that are
// blocked on io_service::run() will try to return as soon as possible, but
// they may still be in the middle of a handler. Thus, perform a join on
// the server thread to guarantee a block occurs.
io_service.stop();
std::cout << "Waiting on server thread..." << std::endl;
server_thread.join();
std::cout << "Done waiting on server thread." << std::endl;
return 0;
}
Во время работы я открыл два подключения. Вот результат:
Stopping service in 10 seconds... Waiting on connection Doing things with socket... Done using socket, ready for another connection. Waiting on connection Doing things with socket... Done using socket, ready for another connection. Waiting on connection Stopping service now! Waiting on server thread... Server thread exiting. Done waiting on server thread.