I want to develop a server use multiple threads and multiple io_service instance(each thread use an io_service instance) to listen at a port. For each io_service instance I create a corresponding boost::asio::ip::tcp::acceptor object. I try this code:
using namespace boost::asio;
...
io_service io_service_;
io_service io_service_1;
ip::tcp::acceptor* acceptor_;
acceptor_ = new ip::tcp::acceptor(io_service_);
ip::tcp::endpoint ep( ip::tcp::v4(), LISTEN_PORT);
acceptor_->open(ep.protocol());
acceptor_->bind(ep);
acceptor_->listen();
ip::tcp::acceptor* acceptor_1;
acceptor_1 = new ip::tcp::acceptor(io_service_1);
acceptor_1->open(ep.protocol());
acceptor_1->bind(ep);
acceptor_1->listen();
...
boost::thread th( boost::bind(&io_service::run, &io_service_));
boost::thread th( boost::bind(&io_service::run, &io_service_1));
...
when running will display error dialog:
boost::exception_detail::clone_impl< boost::exception_detail::error_info_injector > at memory location 0x001FF704.
can you help me how to make a server with Multiple threads, each thread use an io_service instance?
Update: as I read in Boost.Asio C++ Network Programming, have 3 way to use io_service with thread:
I can implement case 1 and 2. But with case 3, I don't know how to implement it to handle many concurrent connections, should I use 1 thread to handle 1 io_service(as above)? Is case 3 has better performance(can handle more concurrent connections) than case 2?
Multithreading, synchronizing, and handler - 2020. The boost. asio starts asynchronous operations rather than calling blocking functions in a sequential manner.
It is safe to post handlers from within a handler for a single instance of an io_service according to the documentation.
Asio multithreaded environment. The only thing you need to get your completion handlers synchronized properly is io_context::strand class instance. It works pretty simple: completion handlers attached to the same io_context::strand will be invoked serially.
Thread Safety Like a regular Boost. Asio socket, a stream is not thread safe. Callers are responsible for synchronizing operations on the socket using an implicit or explicit strand, as per the Asio documentation.
Calling run () repeatedly on a single I/O service object is the recommended method to make a program based on Boost.Asio more scalable. However, instead of providing several threads to one I/O service object, you could also create multiple I/O service objects.
If the member function run () is called on an object of type boost::asio::io_service, the associated handlers are invoked within the same thread. By using multiple threads, a program can call run () multiple times. Once an asynchronous operation is complete, the I/O service object will execute the handler in one of these threads.
Programs that use Boost.Asio for asynchronous data processing are based on I/O services and I/O objects. I/O services abstract the operating system interfaces that process data asynchronously. I/O objects initiate asynchronous operations.
If threads are used, several functions can be executed concurrently on available CPU cores. Boost.Asio with threads improves the scalability because your program can take advantage of internal and external devices that can execute operations independently or in cooperation with each other.
You can use multiple threads, but you need to use 1 acceptor for a port.
IO services are thread-safe, so you can use one service on many threads.
You can even have multiple io services, no problem. You can't bind multiple acceptors to the same port though (unless you bind to different logical network interfaces, perhaps, but that's not really "the same port" then).
#include <boost/asio.hpp>
#include <boost/thread.hpp>
using namespace boost::asio;
using namespace boost;
int main() {
io_service io_service_;
ip::tcp::acceptor acceptor_(io_service_, { {}, 6767 });
acceptor_.bind({ ip::tcp::v4(), 6767 });
boost::thread_group tg;
for (unsigned i = 0; i < thread::hardware_concurrency(); ++i)
tg.create_thread(boost::bind(&io_service::run, &io_service_));
// ...
//
tg.join_all();
}
You can only have one acceptor
listening on a given port. If you want to handle multiple clients with one port, you'll need to set the option reuse_address
on your acceptor
. That way the socket you pass to async_accept()
will use a different port, leaving your listen port free to accept a connection from another client.
boost::asio::io_service ios_;
boost::asio::ip::tcp::acceptor acceptor_(ios_);
void handle_client(boost::shared_pointer<boost::asio::ip::tcp::socket> sock);
void start_accept();
int main() {
boost::asio::ip::tcp::endpoint ep(ip::tcp::v4(), LISTEN_PORT);
acceptor_.open(ep.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
acceptor_.bind(ep);
acceptor_.listen();
boost::thread_group threads;
for (int i = 0; i < NUM_WORKER_THREADS; ++i) {
threads.create_thread(boost::bind(&io_service::run, &ios_));
}
start_accept();
threads.join_all();
}
void start_accept() {
boost::shared_pointer<boost::asio::ip::tcp::socket> sock(ios_);
acceptor_.async_accept(*sock, boost::bind(handle_client, sock));
}
void handle_client(boost::shared_pointer<boost::asio::ip::tcp::socket> sock) {
start_accept();
// talk to the client
}
See Boost's HTTP server example for a more complete example.
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