2

I am learning Boost.Asio. I created a simple program to resolve a host name to IP address. It works fine when using the synchronous resolve operation. However, when I try the asynchronous way, there is some strange behavior.

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

using boost::asio::ip::tcp;

void resolver_handler(
    const boost::system::error_code& err,
    tcp::resolver::iterator it
) {
    if (err) {
        std::cerr << "Resolver error: " << err.message() << std::endl;
        return;
    }
    tcp::resolver::iterator end;
    while (it != end) {
        std::cout << "Host name: " << it->host_name() << std::endl;
        std::cout << "Endpoint: " << it->endpoint() << std::endl;
        std::cout << "Service name: " << it->service_name() << std::endl;
        ++it;
    }
}

void resolve_host(boost::asio::io_service& io_service) {
    tcp::resolver::query query("www.google.com", "http");
    tcp::resolver resolver(io_service);
    resolver.async_resolve(
        query,
        boost::bind(
            resolver_handler,
            boost::asio::placeholders::error,
            boost::asio::placeholders::iterator
        )
    );
    std::cout << "Bind" << std::endl;  // <<<----This line
}

int main(int argc, char **argv) {
    try {
        boost::asio::io_service io_service;
        resolve_host(io_service);
        io_service.run();
    } catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    }
    return 0;
}

When the last line in resolve_host function is commented out, it reports

Resolver error: The I/O operation has been aborted because of either a thread exit or an application request

When that line is present, it give the correct output

Bind
Host name: www.google.com
Endpoint: 216.58.219.4:80
Service name: http

What I did is printing out something. I tried to add some simpler logic after the async_resolve call, (e.g. int a = 1;), it doesn't work. It seems to me this is a timing issue. Maybe something exits too quickly.

I search for this error message but found most posts are about C#. I believe this error message is not from Boost but from Windows system.

Can anyone explain to me why this happens? Thanks a lot.

kenba
  • 4,303
  • 1
  • 23
  • 40
kebugcheck
  • 153
  • 2
  • 11
  • 2
    When `resolve_host` returns, the `tcp::resolver` no longer exists because you created it on the stack that's being destroyed. How is it supposed to execute the query asynchronously? Something has to own that resolver. There are lots of choices, but something has to do it. – David Schwartz Nov 12 '17 at 21:24
  • @DavidSchwartz Aha, that's right. Thank you. – kebugcheck Nov 12 '17 at 22:11

1 Answers1

1

Like @David Schwartz said, you have to keep the resolver alive for the async operation to complete.

Here's the simplest thing I can think of that could work:

void resolve_host(boost::asio::io_service& io_service) {
    tcp::resolver::query query("www.google.com", "http");
    auto resolver = std::make_shared<tcp::resolver>(io_service);
    resolver->async_resolve(
        query,
        [resolver](auto ec, auto it) { resolver_handler(ec, it); }
    );
}

Notice I used a lambda to capture the resolver (which is allocated dynamically instead of on the stack).

If you don't have that option, the easiest way would be to bind some opaque context into the completion handler bind:

using opaque_context = std::shared_ptr<void const>;

void resolver_handler(
    const boost::system::error_code& err,
    tcp::resolver::iterator it,
    opaque_context
) {
    // ...

And then use

resolver->async_resolve(
    query,
    boost::bind(resolver_handler, ph::error, ph::iterator, resolver)
);

See both Compiling On Coliru: c++03 only, C++11 opaque_context, c++11 lambda

Note that I'd probably pass the endpoint instead of the iterator to decouple code more (see e.g. ASIO getting a tcp endpoint directly from an asynchronous resolve). But you don't need to, because the iterator does keep the associated state alive: What's the lifetime of boost::asio::ip::tcp::resolver::iterator from async_resolve?

sehe
  • 374,641
  • 47
  • 450
  • 633