Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between resolving a query and creating an endpoint with IP and port (in boost asio)

I've recently noticed a problem with my application and I think it's due to the fact that I don't use boost::asio properly and don't understand what a tcp resolver does.

Basically, I use a boost::asio::ip::tcp::resolver to get endpoints to connect to.

What I found out recently is that it can come up with more than one endpoint (in particular when I connect to the localhost).

At the moment I request an async_connect on all end point. I'm not a 100% sure but I think that's bad. I should go to them one by one an request an async_connect, wait for the reply and try on the next one if and only if it failed.

So basically knowing that I have two choices if I want to use async_connect on those end points:

  1. refactor my code so that my async_connect handle failure properly and on failure try to connect to the other available endpoint. I would have to pass the endpoint iterator then.

  2. Drop the resolver and use a endpoint I construct myself like this: boost::asio::ip::tcp::endpoint("localhost", 20015)

I kind of have a feeling that I should use the first solution and that the resolver is bringing something more than the self constructed endpoint.

But what does the resolver bring, and how does the self constructed endpoint resolve it self?

like image 376
0x26res Avatar asked Jul 03 '13 07:07

0x26res


2 Answers

While Sam succinctly answered how most applications handle endpoint creation, I wanted to expand upon the resolver.

The resolver is used to convert human-readable text representations of an address into endpoint(s) that contains the structured binary format for an address via hostname resolution or conversions between defined representations. For example, the resolver may resolve the human-readable "localhost" to 0x7F000001 or convert "127.0.0.1" into 0x7F000001. Boost.Asio uses or emulates getaddrinfo() to perform this resolution. For asynchronous resolution, an internal thread will be created to perform the operation.

On the other hand, a basic_endpoint does not resolve itself. While it cannot be constructed with a string and port, it can be constructed with an ip::address and port. The ip::address can be constructed from a string in dotted decimal form (IPv4) or hexadecimal notation (IPv6):

namespace ip = boost::asio::ip;
ip::tcp::endpoint(ip::address::from_string("127.0.0.1"), 20015);

Providing a hostname to ip::address::from_string() will throw an exception:

namespace ip = boost::asio::ip;
ip::address::from_string("localhost"); // throws boost::system::system_error

In the end, use:

  • resolver when you want to support hostname resolution or IP conversions. This is especially convenient when IPs may change but hostnames remain the same, or when a single hostname may resolve to multiple IPs.
  • ip::address::from_string() to create an address from an IP.
like image 53
Tanner Sansbury Avatar answered Nov 09 '22 14:11

Tanner Sansbury


You are correct that connecting to all endpoints returned from the resolve operation is not likely what your application expects. Choice #2 will not work since there is no constructor for a basic_endpoint(const char*, int). Choice #1 is how several asio examples are structured, notably the async tcp client demonstrating timeouts.

Refactoring your code to include the endpoint_iterator returned from the resolver should not be too difficult, follow the example above if you have trouble. Note that it uses a blocking resolve() and not async_resolve(), the concept is the same however.

like image 28
Sam Miller Avatar answered Nov 09 '22 16:11

Sam Miller