Search code examples
c++boostboost-asioasio

boost.asio async_receive never resolve


I'm trying to learn how asio works by implementing an echo server. But it never echo anything!

This is my code (please ignore the resource leak):

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

boost::asio::io_service ioService;

void echo(boost::asio::ip::tcp::socket* socket) {
    char* buf = new char[1024];
    socket->async_receive(boost::asio::buffer(buf, 1023), [buf, socket](auto ec, auto s) {
        if (ec) {
            std::printf("read failed: %s!\n", ec.message().data());
        }
        else
        {
            buf[s] = '\0';
            std::printf("read:%s!\n", buf);
            socket->async_send(boost::asio::buffer(buf, s), [socket](auto ec, auto s) {
                if (ec) {
                    std::printf("write failed: %s!\n", ec.message().data());
                }
                else {
                    echo(socket);
                }
            });
        }
    });
}

void accept(boost::asio::ip::tcp::acceptor& acceptor) {
    auto socket = new boost::asio::ip::tcp::socket{ ioService };
    acceptor.async_accept(*socket, [socket](auto ec) {
        if (ec) {
            std::printf("accept failed:%s!\n", ec.message().data());
        }
        else {
            std::printf("accept %s!", socket->remote_endpoint().address().to_string().data());
            echo(socket);
        }
    });
}

int main() {
    try {
        boost::asio::ip::tcp::acceptor acceptor{ ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 8000) };
        accept(acceptor);
        while (true)
        {
            ioService.poll();
        }
    }
    catch (std::exception&e) {
        std::printf("error: %s\n", e.what());
    }
}

I'm using a Java TCP Client(which I have already tested with a Java TCP echo server) to connect and send message to this sever. The only thing that works is the accept function. Where might I get wrong?


Solution

  • The problem is:

        while (true)
        {
            ioService.poll();
        }
    

    What you want is to remove the while loop and use the "run" method:

         ioService.run();
    

    The actual problem is that you must call the "restart" method (or the older reset method for older versions of asio, which looks to be the case in your code) before you can call poll again. The other problem with the loop is that when there is nothing to do, the CPU it's running on will be at 100% as it's in a hard loop doing nothing... If you use the "run" method, the CPU will be using 0% when there is nothing to do.

    e.g.

    while (true)
    {
        ioService.poll();
        ioService.reset();
    }
    

    To quote the ASIO documentation:

    This function must be called prior to any second or later set of invocations of the run(), run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_context being stopped or running out of work. After a call to restart(), the io_context object's stopped() function will return false.