oldjohn1994 oldjohn1994 - 3 months ago 16
C++ Question

io_service.run() isn't blocking. Server is created and then closes instantly

All of the boost examples work until I try to implement the exact same thing myself. I'm starting to think there must be an order of creation or io_service ownership for things to block properly.

My server structure is as follows:

class Server {
public:
Server(unsigned short port)
: ioService_(), acceptor_(ioService_), socket_(ioService_) {
acceptClient(); // begin async accept
}

void start(); // runs ioService_.run();

private:

void acceptClient();

asio::io_service ioService_;
tcp::acceptor acceptor_;
tcp::socket socket_;
Cluster cluster_; // essentially just a connection manager
};


The acceptClient() function works like this:

void Server::acceptClient() {
acceptor_.async_accept(socket_, [this](const system::error_code& e){
if(!acceptor_.is_open()) return;
if(!e) {
cluster_.add(std::make_shared<Client>(std::move(socket_), cluster_));
}

acceptClient();
});
}


I'm not sure if you need an outline of the Client class since the server should run and block even with no clients.

The creation of the server goes as follows:

try {
Server server(port);
server.start(); // this calls the server's member io_service's run();
} catch (const std::exception& e) {
std::cerr << e.what(); << std::endl;
}


The problem is the server instantly closes after that call. The program starts and then exits with no errors. Is there something that io_service.run() relies on? e.g. some form of asynchronous link that I've forgotten? My learned this design from boost asio's http server design but I've worked it to fit my basic purposes. The problem is some boost examples establish a new member boost tcp::socket in the client itself rather than moving the server's to the client so I'm quite confused. They also tend to use boost's versions of std::bind instead of lambdas which etc.

So, can anyone give me a brief rundown on how to create a basic, stripped, async server since the boost examples are really confusing since the code conventions differ per example. I was wondering if anybody noticed anything straight away that would cause my server to instantly close.

Thanks.

Answer

I tested async_accept with the following code which sends Hello to clients connecting to the port. At least there is the creation of endpoint object, acceptor.open(endpoint.protocol()), acceptor.bind(endpoint) and acceptor.listen() calls that seem to be missing from your code.

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

using namespace boost::asio;

void handle_accept(
    io_service * ios,
    ip::tcp::acceptor * acceptor,
    ip::tcp::socket * socket,
    const boost::system::error_code & error)
{
    if (!error) {
        std::string msg("Hello\n");
        socket->send(buffer(msg, msg.length()));

        ip::tcp::socket * temp = new ip::tcp::socket(*ios);
        acceptor->async_accept(*temp,
                               boost::bind(handle_accept,
                                           ios, acceptor, temp,
                                           placeholders::error));
    }
}

int main(void)
{
    io_service ios;
    ip::tcp::socket socket(ios);
    ip::tcp::acceptor acceptor(ios);
    ip::tcp::endpoint endpoint(ip::tcp::v4(), 1500);
    acceptor.open(endpoint.protocol());
    acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
    acceptor.bind(endpoint);
    acceptor.listen();

    acceptor.async_accept(socket,
                          boost::bind(handle_accept,
                                      &ios, &acceptor, &socket,
                                      placeholders::error));
    ios.run();

    /*
    acceptor.accept(socket);
    std::string msg("Hello\n");
    socket.send(buffer(msg, msg.length()));
    */
}

A version with a Server class and a lambda as a argument for async_accept:

#include <boost/asio.hpp>
#include <functional>
#include <string>

using namespace boost::asio;

class Server {
public:
    Server(unsigned short port) : ios(), acceptor(ios), socket(ios), 
                                  endpoint(ip::tcp::v4(), port) {
        acceptor.open(endpoint.protocol());
        acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
        acceptor.bind(endpoint);
        acceptor.listen();
        nsocket = &socket;
    }

    void run() {
        std::function<void (const boost::system::error_code &)> f;
        f = [&f, this] (const boost::system::error_code & error) {
            if (!error) {
                std::string msg("Hello\n");
                nsocket->send(buffer(msg, msg.length()));

                nsocket = new ip::tcp::socket(ios);
                acceptor.async_accept(*nsocket, f);
            }
        };

        acceptor.async_accept(socket, f);
        ios.run();
    }

protected:
    io_service ios;
    ip::tcp::acceptor acceptor;
    ip::tcp::socket socket;
    ip::tcp::endpoint endpoint;

    ip::tcp::socket * nsocket;
};


int main(void)
{
    Server srv(1500);
    srv.run();
}
Comments