Acceptor and Problems with Async_Accept

11,355

Solution 1

So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?

Why do you think this is happening? If you posted the complete code, that would greatly help. When I take your snippet and put a boilerplate main and io_service::run() around it, everything works fine.

#include <boost/asio.hpp>
#include <boost/bind.hpp>

#include <iostream>

using namespace boost::asio;

class Socket {
public:
    Socket(
            io_service& io_service
          ) :
        _io_service( io_service ),
        _acceptor( new ip::tcp::acceptor(io_service) )
    {

    }

    bool start(int port)
    {
        //try to listen
        ip::tcp::endpoint endpoint(ip::tcp::v4(), port);
        _acceptor->open(endpoint.protocol());
        _acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
        _acceptor->bind(endpoint);
        //CAN GET NEW CONNECTIONS HERE (before async_accept is called)
        _acceptor->listen();

        ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
        _acceptor->async_accept(
                *temp,
                boost::bind(
                    &Socket::NewConnection,
                    this,
                    temp,
                    boost::asio::placeholders::error
                    )
                );
    }

    void NewConnection(
            ip::tcp::socket* s, 
            const boost::system::error_code& error
            )
    {
        std::cout << "New Connection Made" << std::endl;
        //Start new accept async
        ip::tcp::socket* temp = new ip::tcp::socket( _io_service );
        _acceptor->async_accept(
                *temp,
                boost::bind(
                    &Socket::NewConnection, 
                    this,
                    temp,
                    boost::asio::placeholders::error
                    )
                );
    }
private:
    io_service& _io_service;
    ip::tcp::acceptor* _acceptor;
};

int
main()
{
    io_service foo;
    Socket sock( foo );
    sock.start(1234);
    foo.run();

    return 0;
}

compile and run:

macmini:~ samm$ g++ -lboost_system accept.cc
macmini:~ samm$ ./a.out 
New Connection Made

telnet from another terminal

macmini:~ samm$ telnet 127.0.0.1 1234
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

Solution 2

I think you are mixing different things here.

On the one hand, you are creating a socket for data exchange. A socket is nothing more than an endpoint of an inter-process communication flow across a computer network. Your boost::asio::tcp::socket uses the TCP-protocoll for the communication; but in general, a socket can use other protocols. For opening a tcp-socket, one uses generally the sequence open-bind-listen-accept on the host.

On the other hand, you analyse the (underlying) TCP-connection.

So there are two different things here. While for the socket the connection is considered "established" only after the "accept" of the host, the underlying TCP-connection is already established after the client connects to a listening socket. (One the server side, that connection is put on a stack, from which it is dequeue when you call accept()).

So the only way to prohibit connection in your case, is not to call listen().

Solution 3

If you are truly getting a new connection at the point when you call acceptor->listen() then I am puzzled by that. What are you using to determine whether you've gotten a connection or not? The io_service is typically quite "reactive" in that it only reacts to events that it has been explicitly told to react to.

In your example above, the only thing I see that would cause a "new connection" to be initiated is calling async_accept. Additionally, what you described makes little sense from a low-level sockets standpoint (using BSD sockets, typically you must call bind, listen, and accept in that order, and only then can a new connection be made).

My suspicion is that you've actually got some faulty logic somewhere. Who calls StartListening and how often is it called (it should only need to be called once). You've gone through a bunch of extra effort to setup your acceptor object that's usually not necessary in Asio - you can typically just use the acceptor constructor to create an acceptor with all the parameters you need, and then just call async_accept:

acceptor = new tcp::acceptor(
    this->myService,
    boost::asio::ip::tcp::endpoint(
        boost::asio::ip::tcp::v4(),
        port),
    true);

tcp::socket* tempNewSocket = new tcp::socket(this->myService);
acceptor->async_accept(
    *tempNewSocket, 
    boost::bind(
        &AlexSocket::NewConnection, 
        this, 
        tempNewSocket, 
        boost::asio::placeholders::error) );
Share:
11,355
bobber205
Author by

bobber205

Graduated from a tech college with a BS in Software Engineering.

Updated on June 15, 2022

Comments

  • bobber205
    bobber205 almost 2 years

    See code. :P I am able to receive new connections before async_accept() has been called. My delegate function is also never called so I can't manage any connections I receive, rendering the new connections useless. ;)

    So here's my question. Is there a way to prevent the Boost ASIO acceptor from getting new connections on its own and only getting connections from async_accept()?

    Thanks!

    AlexSocket::AlexSocket(boost::asio::io_service& s): myService(s)
    {
        //none at the moment
        connected = false;
        listening = false;
    
        using boost::asio::ip::tcp;
        mySocket = new tcp::socket(myService);
    }
    
    AlexSocket::~AlexSocket()
    {
        delete mySocket;
    }
    
    bool AlexSocket::StartListening(int port)
    {
        bool didStart = false;
    
        if (!this->listening)
        {
            //try to listen
            acceptor = new tcp::acceptor(this->myService);
            boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
            acceptor->open(endpoint.protocol());
            acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
            acceptor->bind(endpoint);
            //CAN GET NEW CONNECTIONS HERE (before async_accept is called)
            acceptor->listen();
    
            didStart = true; //probably change?
            tcp::socket* tempNewSocket = new tcp::socket(this->myService);
            //acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
        }
        else //already started!
            return false;
    
        this->listening = didStart;
        return didStart;
    }
    
    //this function is never called :(
    void AlexSocket::NewConnection(tcp::socket* s, const boost::system::error_code& error)
    {
        cout << "New Connection Made" << endl;
        //Start new accept async
        tcp::socket* tempNewSocket = new tcp::socket(this->myService);
        acceptor->async_accept(*tempNewSocket, boost::bind(&AlexSocket::NewConnection, this, tempNewSocket, boost::asio::placeholders::error) );
    }
    
    bool AlexSocket::ConnectToServer(std::string toConnectTo, string port)
    {
        if (connected)
            return false;
    
        this->serverConnectedTo = toConnectTo;
        this->serverPort = port;
    
        ip::tcp::resolver resolver(myService);
        ip::tcp::resolver::query newQuery(toConnectTo, port);
        ip::tcp::resolver::iterator myIter = resolver.resolve(newQuery);
        ip::tcp::resolver::iterator end;
    
        //error
        boost::system::error_code error = boost::asio::error::host_not_found;
    
        //try each endpoint
        bool connected = false;
        while (error && myIter != end)
        {
            ip::tcp::endpoint endpoint = *myIter++;
            std::cout << endpoint << std::endl;
    
            mySocket->close();
            mySocket->connect(*myIter, error);
    
            if (error)
            {
                //try to connect, if it didn't work return false
                cout << "Did not Connect" << endl << error << endl;
            }
            else
            {
                //was able to connect
                cout << "Connected!" << endl;
                connected = true;
            }
            myIter++;
        }   
        this->connected = connected;
        return connected;
    }
    

    EDIT: I've changed my code to reflect what the answers so far have said. I am passing in an io_service to the ctor of my class. As you can see below, main is NOT calling run on the service, so I would assume that nothing should be able to connect right?

    I have put my debugger on the listen() line and went to "canyouseeme.org". Typed in 57422 and hit Connect. Couldn't. Ran the listen() line. Was able to connect. This shouldn't be possible right? Like never? :(

    No idea what to do anymore. main() is below.

    int main()
    {
        boost::asio::io_service s;
        AlexSocket test(s);
    
        test.StartListening(57422);
    
        test.ConnectToServer("localhost", "57422");
    
        cout << "Enter something to quit" << endl;
        int a2;
        cin >> a2;
    
        return 0;
    }