Boost Asio, Multiple threads and multiple io_service
Asked Answered
F

2

9

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:

  1. Single-thread with one io_service and one handler thread(thread running io_service::run())
  2. Multi-threaded with a single io_service instance and several handler threads
  3. Multi-threaded with several io_service instances and several threads

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?

Freeholder answered 4/11, 2015 at 18:20 Comment(2)
You could of course catch the exception and handle the error to know what causes itDispel
Got sidetracked, but here you go: coliru.stacked-crooked.com/a/7cd950538f167a90Dispel
D
11

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();
}
Dispel answered 4/11, 2015 at 18:56 Comment(7)
'"IO services are thread-safe, so you can use one service on many threads." I know it, but I want to use multiple thread - multiple io_service, please see my updateFreeholder
That's what I said: boost.org/doc/libs/1_59_0/doc/html/boost_asio/reference/…Dispel
@Freeholder (to the edited comment) - you can provided that you can listen on separate ports/interfaces. I suppose you could use sockets on differen io_service than acceptor_, but your sample program didn't have sockets.Dispel
Can I use many io_service instance to listen on 1 port? does it improve performance? I don't want to use many ports because it will be inconvenience for clientFreeholder
Did you read the answer? Or that by Miles? No you cannot use many io_service instances to "listen" on 1 port. You can only have 1 acceptor per port, and the acceptor is tied to 1 io_service. You can repeat the question, but the answer stays the same.Dispel
@Freeholder whether it'll be inconvenient depends on your routing/load balancing. It probably won't be faster anyways. You can use multiple ports if you insist on benchmarking it.Dispel
sorry, I've not read the answer carefully, I just want to confirm that there's no way to use many io_service instances to listen on 1 port. thanks! My program has socket, the above code is just a part of my program.Freeholder
C
5

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.

Conveyancer answered 4/11, 2015 at 19:44 Comment(3)
Your answer seems to be exactly what I said an hour earlier.Dispel
The SO_REUSEADDRESS option have nothing to do with accepting multiple simultaneous connections, that's allowed anyway (by assigning different port numbers to each accepted connection). Instead the SO_REUSEADDR option allows you to bind a socket to an address/port already bound to another socket which is in the TIME_WAIT state. That means you can close the socket, create a new, and bind it to the same address/port without getting an error.Notornis
I think what you are talking about here is SO_REUSEPORT.Alarm

© 2022 - 2024 — McMap. All rights reserved.