I am using boost::asio::ip::udp::resolver in an SNMPV2 implementation to determine wheather a host is reachable or not.
using Resolver = boost::asio::ip::udp
As noted in the documentation, Boost.Asio will create an an additional thread per io_service to emulate asynchronous host resolution on the first call to resolver::async_resolve().
Creating multiple io_service objects will allow for concurrent host resolution only when initiating asynchronous resolution operations on resolvers associated with different io_services. For example, the following code will not perform concurrent host resolution, as both resolvers use the same service:
boost::asio::io_service service1;
boost::asio::ip::udp::resolver resolver1(service1); // using service1
boost::asio::ip::udp::resolver resolver2(service1); // using service1
resolver1.async_resolve(...);
resolver2.async_resolve(...);
On the other hand, the following will perform concurrent host resolution, as each resolver is using a different service:
boost::asio::io_service service1;
boost::asio::io_service service2;
boost::asio::ip::udp::resolver resolver1(service1); // using service1
boost::asio::ip::udp::resolver resolver2(service2); // using service2
resolver1.async_resolve(...);
resolver2.async_resolve(...);
Assuming a resolver per io_service, to obtain concurrency, it becomes the applications responsibility to dispatch resolution operations to different resolvers. A simple work distribution strategy, such as round-robin, may suffice.
On the other hand, one can delegate this responsibility to an io_service, allowing it to distribute work that will emulate asynchronous host resolution in a similar manner to what Boost.Asio does internally. The synchronous resolver::resolve() member function performs work in the calling thread. Thus, an application could create an io_service that is serviced by a thread pool. When asynchronous host resolution needs to occur, a job is posted into the io_service that will create create a resolver and perform synchronous resolution, invoking the user handler with the results. Below is a complete basic example where a resolver class emulates asynchronous host resolution with a thread pool:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>
/// @brief Type used to emulate asynchronous host resolution with a
/// dedicated thread pool.
class resolver
{
public:
resolver(const std::size_t pool_size)
: work_(boost::ref(io_service_))
{
// Create pool.
for (std::size_t i = 0; i < pool_size; ++i)
threads_.create_thread(
boost::bind(&boost::asio::io_service::run, &io_service_));
}
~resolver()
{
work_ = boost::none;
threads_.join_all();
}
template <typename QueryOrEndpoint, typename Handler>
void async_resolve(QueryOrEndpoint query, Handler handler)
{
io_service_.post(boost::bind(
&resolver::do_async_resolve<QueryOrEndpoint, Handler>, this,
query, handler));
}
private:
/// @brief Resolve address and invoke continuation handler.
template <typename QueryOrEndpoint, typename Handler>
void do_async_resolve(const QueryOrEndpoint& query, Handler handler)
{
typedef typename QueryOrEndpoint::protocol_type protocol_type;
typedef typename protocol_type::resolver resolver_type;
// Resolve synchronously, as synchronous resolution will perform work
// in the calling thread. Thus, it will not use Boost.Asio's internal
// thread that is used for asynchronous resolution.
boost::system::error_code error;
resolver_type resolver(io_service_);
typename resolver_type::iterator result = resolver.resolve(query, error);
// Invoke user handler.
handler(error, result);
}
private:
boost::asio::io_service io_service_;
boost::optional<boost::asio::io_service::work> work_;
boost::thread_group threads_;
};
template <typename ProtocolType>
void handle_resolve(
const boost::system::error_code& error,
typename ProtocolType::resolver::iterator iterator)
{
std::stringstream stream;
stream << "handle_resolve:\n"
" " << error.message() << "\n";
if (!error)
stream << " " << iterator->endpoint() << "\n";
std::cout << stream.str();
std::cout.flush();
}
int main()
{
// Resolver will emulate asynchronous host resolution with a pool of 5
// threads.
resolver resolver(5);
namespace ip = boost::asio::ip;
resolver.async_resolve(
ip::udp::resolver::query("localhost", "12345"),
&handle_resolve<ip::udp>);
resolver.async_resolve(
ip::tcp::resolver::query("www.google.com", "80"),
&handle_resolve<ip::tcp>);
resolver.async_resolve(
ip::udp::resolver::query("www.stackoverflow.com", "80"),
&handle_resolve<ip::udp>);
resolver.async_resolve(
ip::icmp::resolver::query("some.other.address", "54321"),
&handle_resolve<ip::icmp>);
}
And annotated output:
handle_resolve:
Success
127.0.0.1:12345 // localhost
handle_resolve:
Service not found // bogus
handle_resolve:
Success
173.194.77.147:80 // google
handle_resolve:
Success
198.252.206.16:80 // stackoverflow
What you need are two io_service ioService, because each one is run by a thread. By that I mean that you block the normal execution of a thread by calling io_service::run.
I think that the code itself is correct.