Boost::asio - how to interrupt a blocked tcp server thread?
In short, there are two options:
- Change code to be asynchronous (
acceptor::async_accept()
andasync_read
), run within the event loop viaio_service::run()
, and cancel viaio_service::stop()
. - Force blocking calls to interrupt with lower level mechanics, such as signals.
I would recommend the first option, as it is more likely to be the portable and easier to maintain. The important concept to understand is that the io_service::run()
only blocks as long as there is pending work. When io_service::stop()
is invoked, it will try to cause all threads blocked on io_service::run()
to return as soon as possible; it will not interrupt synchronous operations, such as acceptor::accept()
and socket::receive()
, even if the synchronous operations are invoked within the event loop. It is important to note that io_service::stop()
is a non-blocking call, so synchronization with threads that were blocked on io_service::run()
must use another mechanic, such as thread::join()
.
Here is an example that will run for 10 seconds and listens to port 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;
}
While running, I opened two connections. Here is the output:
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.
Thread-safely closing a boost::asio::ip::tcp::socket being used synchronously
Having spent some time looking into this there is only 1 thread safe manner in which this can be achieved: by sending a message to the socket (on a thread not waiting on accept()
) telling the thread to close the socket and the acceptor. By doing this the socket and acceptor can be wholly owned by a single thread.
As pointed out separately, io_service
is only of use for asynchronous operations.
boost asio write operation on client is blocked when server down
There's nothing wrong with your client code, it is behaving correctly as a TCP socket implements reliable in-order delivery of the byte stream. Your client does not realize the server is unresponsive because that's not such a simple task, there are many entities between your client and server applications.
You may wish to enable TCP keep alive. Though be careful as it too is not a silver bullet, it merely indicates the TCP stack on both ends is alive and well; which says nothing about the application availability. The various timeous are also somewhat tricky to configure for all environments.
The most flexible option is to implement a heartbeat protocol in your client and server, which can be fully tailored to your application's performance requirements.
[boost.asio]closing tcp::socket or tcp::acceptor in different thread from the I/O thread
The documentation states that tcp::socket
is not thread safe for shared objects.
Don't count on it seemingly working as a guarantee that it will always work.
Moreover, closing a socket from another thread, at the socket layer, is not a portable way of getting the blocking thread to unblock.
Here's what I would suggest:
- Use the asynchronous APIs for asio.
- Protect the socket with a mutex to prevent concurrent access or use an asio strand to serialize the access to it.
Quoting the author on a similar question:
Actually...
In practice, it will probably work on the platforms asio currently
supports**. However, I have deliberately specified the interface such
that it is not thread safe. This is to permit implementations to
store additional state in the socket object without needing explicit
synchronisation.If you want to run multiple concurrent operations on the same socket,
the safe, portable way is to use the asynchronous operations.**Unless you make the socket non-blocking.
Cheers, Chris
Related Topics
C++ Overloading Array Operator
Double Delete in Initializer_List VS 2013
Why Is a Constexpr Function on a Reference Not Constexpr
What Is the Proper Function for Comparing Two C-Style Strings
Errors When Linking to Protobuf 3 on Ms Visual C
How to Find a Search Term in Source Code
Static Member Access in Constant Expressions
Why Does C++ Allow Variable Length Arrays That Aren't Dynamically Allocated
At What Point Does Dereferencing the Null Pointer Become Undefined Behavior
Why Does Boost.Asio Not Support an Event-Based Interface
Invalid Use of Incomplete Type
How to Alter a Float by Its Smallest Increment (Or Close to It)
String Literals Not Allowed as Non Type Template Parameters
What Happens When I Mix Signed and Unsigned Types
How to Clear the Console in Both Windows and Linux Using C++