ASIO getting a tcp endpoint directly from an async

2020-04-19 06:20发布

I'm looking to use the ASIO standalone library (not Boost ASIO), I am trying to set up a client to connect to a server on a specific port.

I saw in the porthopper example that it is possible to get the endpoint without having to deal with an iterator.

asio::io_service io_service;

// Determine the location of the server.
tcp::resolver resolver(io_service);
tcp::resolver::query query(host_name, port);
tcp::endpoint remote_endpoint = *resolver.resolve(query);

I am trying to do the resolve of the query using resolver async_resolve() member function.

This is the code I currently have:

asio::io_service IOService;
asio::ip::tcp::resolver resolver(IOService);
asio::ip::tcp::resolver::query query(ADDRESS, PORT);
resolver.async_resolve(query,
            [this](const tcp::endpoint srvEndpoint, std::error_code error)
            {
                IOService->post(
                    [this, error, srvEndpoint]
                    {
                         handle_resolve_handler(error, srvEndpoint);
                    });
            });

Is there a way to do what was shown in the porthopper example but perform it asynchronously?

1条回答
何必那么认真
2楼-- · 2020-04-19 07:10

Oh, but

tcp::endpoint remote_endpoint = *resolver.resolve(query);

deals with the iterator very much! It uses it to dereference. Note that cute star? It's the pointer indirection operator.

As for your call:

resolver.async_resolve(query,
            [this](const tcp::endpoint srvEndpoint, std::error_code error)
            {
                IOService->post(
                    [this, error, srvEndpoint]
                    {
                         handle_resolve_handler(error, srvEndpoint);
                    });
            });

That does not satisfy the completion handler requirements. Indeed, trying to compile it with Boost Asio² gives a slew of errors: Live On Coliru:

main.cpp:12:14:   required from here
/usr/local/include/boost/asio/ip/basic_resolver.hpp:163:5: error: static assertion failed: ResolveHandler type requirements not met
     BOOST_ASIO_RESOLVE_HANDLER_CHECK(
     ^
/usr/local/include/boost/asio/ip/basic_resolver.hpp:163:5: error: no match for call to '(Demo::doResolve()::<lambda(boost::asio::ip::tcp::endpoint, boost::system::error_code)>) (const boost::system::error_code&, const boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp>&)'
     BOOST_ASIO_RESOLVE_HANDLER_CHECK(

The docs say:

enter image description here

Lo and behold, there's your iterator again! This is by no means an accident. The design of the library is such that async calls will always return the same data regardless of the chosen interface.¹

Cobbling it together: Live On Coliru

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

namespace asio = boost::asio;
using boost::system::error_code;
using asio::ip::tcp;

struct Demo {
    Demo(asio::io_service& svc) : _svc(svc) {}
    void doResolve() {
        resolver.async_resolve(query, [this](error_code error, tcp::resolver::iterator it) {
                tcp::endpoint ep = error? tcp::endpoint{} : *it;
                _svc.post([this, error, ep] { handle_resolve_handler(error, ep); });
            });
    }

  private:
    asio::io_service& _svc;
    tcp::resolver resolver     {_svc};
    tcp::resolver::query query {"www.google.com", "https"};

    void handle_resolve_handler(error_code ec, tcp::endpoint srvEndpoint) {
        std::cout << "handle_resolve_handler: " << ec.message() << " " << srvEndpoint << "\n";
    }
};

int main() {
    asio::io_service svc;
    Demo x(svc);
    x.doResolve();

    svc.run();
}

Prints³:

handle_resolve_handler: Success 216.58.213.196:443

¹ cf. the difference when using coroutines (yield or yield[ec]), asio::use_future etc.: How to set error_code to asio::yield_context

² basically s/boost::system::error_code/std::error_code/

³ On systems with network access

查看更多
登录 后发表回答