I'm currently writing a multi-threaded server where each thread has an io_context and a list of task objects to execute, with each task object having an associated ip::tcp::socket object.
For load-balancing I'm sometimes migrating tasks from one thread to another, however I'd like to migrate their sockets as well without dropping connection.
I could simply pass ownership of the socket object between threads, however the socket's io_context will remain that of its original thread, which would add significant complexity/slow-down.
Is there any way for me to keep a socket connection whilst moving it to a different io_context? Or is there another recommended approach?
Many thanks
The io_context class provides the core I/O functionality for users of the asynchronous I/O objects, including: boost::asio::ip::tcp::socket.
Thread Safety In general, it is safe to make concurrent use of distinct objects, but unsafe to make concurrent use of a single object. However, types such as io_context provide a stronger guarantee that it is safe to use a single object concurrently.
At its core, Boost Asio provides a task execution framework that you can use to perform operations of any kind. You create your tasks as function objects and post them to a task queue maintained by Boost Asio. You enlist one or more threads to pick these tasks (function objects) and invoke them.
You can not change the io_context directly, but there's a workaround
just use release and assign
here's an example:
const char* buff = "send";
boost::asio::io_context io;
boost::asio::io_context io2;
boost::asio::ip::tcp::socket socket(io);
socket.open(boost::asio::ip::tcp::v4());
std::thread([&](){
auto worker1 = boost::asio::make_work_guard(io);
io.run();
}).detach();
std::thread([&](){
auto worker2 = boost::asio::make_work_guard(io2);
io2.run();
}).detach();
socket.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string("127.0.0.1"), 8888));
socket.async_send(boost::asio::buffer(buff, 4),
[](const boost::system::error_code &ec, std::size_t bytes_transferred)
{
std::cout << "send\n";
fflush(stdout);
});
// any pending async ops will get boost::asio::error::operation_aborted
auto fd = socket.release();
// create another socket using different io_context
boost::asio::ip::tcp::socket socket2(io2);
// and assign the corresponding fd
socket2.assign(boost::asio::ip::tcp::v4(), fd);
// from now on io2 is the default executor of socket2
socket2.async_send(boost::asio::buffer(buff, 4),
[](const boost::system::error_code &ec, std::size_t bytes_transferred){
std::cout << "send via io2\n";
fflush(stdout);
});
getchar();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With