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:
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.
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?
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.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With