Thanks for the suggestions, Gavin! I feel as though I've made some progress. However, my app is still "hanging" when executing the io_service. Here is a full example of my code:(Sorry if this is long)
I am using wxWidgets for my platform so if your familiar at all with it you'll see I'm basically reading from a listview a list of ip's.....
int TOTAL_ITEMS = lstUsers->GetItemCount();
int TOTAL_FINISHED = 0;
int MAX_THREADS = 4;
if ( TOTAL_ITEMS < MAX_THREADS )
MAX_THREADS = TOTAL_ITEMS;
std::vector < XPing* > p;
std::vector< std::string> ping_results (lstUsers->GetItemCount());
std::vector< boost::thread* > threads;
boost::asio::io_service io_;
wxString ip_;
wxCharBuffer buffer;
//create ping objects
for ( int i=0; i <TOTAL_ITEMS; ++i)
{
ip_ = lstUsers->GetItemText(i, 3);
buffer = ip_.ToUTF8();
if ( ! (buffer.data()[0]=='0' ) )
{
//ip *should* be valid
try
{
p.push_back( new XPing ( io_, buffer.data(), boost::ref(ping_results), TOTAL_FINISHED) );
}
catch ( std::exception & e )
{
wxMessageBox(e.what());
}
TOTAL_FINISHED++;
}
}
//create threads
for ( int i = 0; i < MAX_THREADS; i++ )
{
threads.push_back( new boost::thread ( boost::bind(&boost::asio::io_service::run, boost::ref(io_) ) ) );
}
//join/delete threads
for ( int i=0; i < MAX_THREADS; i++ )
{
threads[i]->join();
delete threads[i];
}
//delete ping objects
for ( int i=0; i < TOTAL_FINISHED; i++ )
{
delete p[i];
}
//populate listview with ping values
for ( int i=0; i<TOTAL_FINISHED; i++ )
{
lstUsers->SetItem(i,11,ping_results[i]);
}
-------------------------------------------------------------------------------------------------------------------
Now, here is the XPing class. I made the resolver async as you suggested and within the resolver handler I start the send/receive functions:
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
class XPing
{
public:
XPing( boost::asio::io_service & io_, const char* destination, std::vector<std::string> & ping_results, int index=0) : ping_results_(ping_results),
ip_addr(destination), /*, work_(new boost::asio::io_service::work(io_service_))*/ strand_(io_), resolver_(io_), socket_(io_, icmp::v4()),
timer_(io_), io_service_(io_), sequence_number_(0), num_replies_(0)
{
socket_.non_blocking(true);
boost::asio::socket_base::reuse_address option(true);
socket_.set_option(option);
_index_ = index;
strand_.post( boost::bind( &XPing::resolve, this) );
}
void resolve()
{
icmp::resolver::query query(icmp::v4(), ip_addr, "");
resolver_.async_resolve(query, boost::bind(&XPing::handle_resolve, this, boost::asio::placeholders::error, boost::asio::placeholders::iterator) );
}
void handle_resolve(const boost::system::error_code& err, icmp::resolver::iterator endpoint_iterator)
{
if ( !err )
{
//icmp::endpoint endpoint = *endpoint_iterator;
destination_ = *endpoint_iterator;
socket_.async_connect( destination_, strand_.wrap( boost::bind(&XPing::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator) ) );
}
else
{
//error
wxMessageBox("There was an error. Code #2");
}
}
void handle_connect( const boost::system::error_code& err, icmp::resolver::iterator endpoint_iterator)
{
if (!err)
{
// The connection was successful. Send the request.
strand_.wrap( boost::bind( &XPing::start_send, this) );
strand_.wrap( boost::bind( &XPing::start_receive, this) );
}
else if (endpoint_iterator != icmp::resolver::iterator())
{
wxMessageBox("There was an error. Code #2");
// The connection failed. Try the next endpoint in the list.
socket_.close();
//icmp::endpoint endpoint = *endpoint_iterator;
destination_ = *endpoint_iterator;
socket_.async_connect( destination_, strand_.wrap( boost::bind(&XPing::handle_connect, this, boost::asio::placeholders::error, ++endpoint_iterator) ) );
}
else
{
//error
wxMessageBox("There was an error. Code #3");
}
}
void Destroy()
{
this->~XPing();
}
std::string GetIP()
{
std::string result = ip_addr;
return result;
}
private:
void start_send()
{
std::string body("\"Hello!\" from Asio ping.");
// Create an ICMP header for an echo request.
icmp_header echo_request;
echo_request.type(icmp_header::echo_request);
echo_request.code(0);
echo_request.identifier(get_identifier());
echo_request.sequence_number(++sequence_number_);
compute_checksum(echo_request, body.begin(), body.end());
// Encode the request packet.
boost::asio::streambuf request_buffer;
std::ostream os(&request_buffer);
os << echo_request << body;
// Send the request.
time_sent_ = posix_time::microsec_clock::universal_time();
try
{
socket_.async_send_to(request_buffer.data(), destination_, strand_.wrap( boost::bind(&XPing::handle_send, this) ) );
}
catch ( std::exception & e )
{
wxMessageBox( e.what() );
}
}
void handle_send()
{
// Wait 500ms
num_replies_ = 0;
timer_.expires_at(time_sent_ + posix_time::milliseconds(500));
timer_.async_wait( strand_.wrap ( boost::bind(&XPing::handle_timeout, this ) ) );
}
void handle_timeout()
{
if (num_replies_ == 0)
{
ping_results_[_index_] = "NO";
timer_.cancel();
}
}
void start_receive()
{
// Discard any data already in the buffer.
reply_buffer_.consume(reply_buffer_.size());
// Wait for a reply. We prepare the buffer to receive up to 64KB.
socket_.async_receive(reply_buffer_.prepare(65536), strand_.wrap( boost::bind(&XPing::handle_receive, this, _2) ) );
}
void handle_receive(std::size_t length)
{
// The actual number of bytes received is committed to the buffer so that we
// can extract it using a std::istream object.
reply_buffer_.commit(length);
// Decode the reply packet.
std::istream is(&reply_buffer_);
ipv4_header ipv4_hdr;
icmp_header icmp_hdr;
is >> ipv4_hdr >> icmp_hdr;
// We can receive all ICMP packets received by the host, so we need to
// filter out only the echo replies that match the our identifier and
// expected sequence number.
if (is && icmp_hdr.type() == icmp_header::echo_reply
&& icmp_hdr.identifier() == get_identifier()
&& icmp_hdr.sequence_number() == sequence_number_)
{
// If this is the first reply, interrupt the five second timeout.
if (num_replies_++ == 0)
{
timer_.cancel();
}
posix_time::ptime now = posix_time::microsec_clock::universal_time();
try
{
ping_results_[_index_] = boost::lexical_cast<std::string>( (now - time_sent_).total_milliseconds() ) + "ms"; //this may need to be locked by mutex?
}
catch (std::exception & e)
{
wxMessageBox( e.what() );
}
}
else if ( (icmp_hdr.sequence_number() == 5) || (icmp_hdr.sequence_number() == 3) || (icmp_hdr.sequence_number() == 11) )
{
if (num_replies_++ == 0)
{
timer_.cancel();
}
try
{
ping_results_[_index_] = "DNS"; //this may need to be locked by mutex?
}
catch (std::exception & e)
{
wxMessageBox( e.what() );
}
}
}
----------------------------------------------------------------------------------------------------------