7

I have a boost::asio based server which works fine, except that I'm trying to add a check that nothing else is accepting connections on the same port. If I create two of the servers, one of them consistently accepts the connections, but the other does not report any error ( which of the two accepts all connections appears to be random ).

The relevant bits of the server class ( it's a template which uses a base class which has Accepted() and typedefs for the connection type to create ) are:

        MessageServer ( boost::asio::io_service &io, unsigned short port_num )
            : BaseServerType ( io ), acceptor_ ( io, boost::asio::ip::tcp::endpoint ( boost::asio::ip::tcp::v4(), port_num ) ) {
            Listen();
        }
        void Listen () {
            boost::system::error_code ec;
            acceptor_.listen ( boost::asio::socket_base::max_connections, ec );

            if ( !ec ) {
                start_accept();
            } else {
                // not reached even if a separate process 
                // is already listening to that port
                connection_pointer new_connection;
                BaseServerType::Accepted ( new_connection );
            }
        }

    private:
        void start_accept() {
            connection_pointer new_connection ( CreateConnection ( acceptor_.io_service() ) );

            acceptor_.async_accept ( new_connection -> Socket(),
                                     boost::bind ( &MessageServer::handle_accept, this, new_connection,
                                                   boost::asio::placeholders::error ) );
        }

        void handle_accept ( connection_pointer new_connection, const boost::system::error_code &error ) {
            if ( !error ) {
                BaseServerType::Accepted ( new_connection );
                new_connection -> Start();
                start_accept();
            } else {
                // never reached
                new_connection.reset();
                BaseServerType::Accepted ( new_connection );
            }
        }

        boost::asio::ip::tcp::acceptor acceptor_;

acceptor.listen() doesn't throw either.

How is a failure to listen to a server port reported in boost::asio?

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
Pete Kirkham
  • 48,893
  • 5
  • 92
  • 171

2 Answers2

9

boost::asio::ip::tcp::acceptor constructor you use has a default argument reuse_address = true. this sets SO_REUSEADDR socket option. disabling this option you'll receive error on acceptor.listen(). more info about SO_REUSEADDR here

Be aware that this option is treated differently on windows and linux

Community
  • 1
  • 1
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112
  • If I translate above description, here is code. MessageServer ( boost::asio::io_service &io, unsigned short port_num ) : BaseServerType ( io ) , acceptor_ ( io, boost::asio::ip::tcp::endpoint ( boost::asio::ip::tcp::v4(), port_num, false ) ) { Listen(); } ... try { server = new MessageServer(io_service_, 5000); } catch (std::exception const& ex) { printf("bind error: %s\n", ex.what()); } – Hill Nov 09 '15 at 08:16
  • From the linked website: "On Windows it lets you bind to the same TCP port multiple times without errors. Verified experimentally by failing test, I never bothered to check out MSDN docs for this one, I just don't use it on Windows." – Arthur Tacca Jul 05 '18 at 12:12
0

The error you want would be thrown or returned from boost::asio::ip::tcp::acceptor::bind(). The constructor you are using will exhibit this same behavior, I suggest you boil your example down to something smaller that is reproducible. It is not obvious to me how you handle exceptions thrown from your MessageServer ctor.

Sam Miller
  • 23,808
  • 4
  • 67
  • 87
  • As with any constructor which can throw, whatever creates the servers either wraps the constructor in try/catch if it is recoverable, or let it percolate to the outer scope if it is not. – Pete Kirkham Feb 15 '11 at 16:57