5 // Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
11 #include <boost/asio.hpp>
12 #include <boost/bind.hpp>
17 #include "icmp_header.hpp"
18 #include "ipv4_header.hpp"
20 using boost::asio::ip::icmp;
21 using boost::asio::deadline_timer;
22 namespace posix_time = boost::posix_time;
27 pinger(boost::asio::io_service& io_service, const char* destination)
28 : resolver_(io_service), socket_(io_service, icmp::v4()),
29 timer_(io_service), sequence_number_(0), num_replies_(0)
31 icmp::resolver::query query(icmp::v4(), destination, "");
32 destination_ = *resolver_.resolve(query);
41 std::string body("\"Hello!\" from Asio ping.");
43 // Create an ICMP header for an echo request.
44 icmp_header echo_request;
45 echo_request.type(icmp_header::echo_request);
47 echo_request.identifier(get_identifier());
48 echo_request.sequence_number(++sequence_number_);
49 compute_checksum(echo_request, body.begin(), body.end());
51 // Encode the request packet.
52 boost::asio::streambuf request_buffer;
53 std::ostream os(&request_buffer);
54 os << echo_request << body;
57 time_sent_ = posix_time::microsec_clock::universal_time();
58 socket_.send_to(request_buffer.data(), destination_);
60 // Wait up to five seconds for a reply.
62 timer_.expires_at(time_sent_ + posix_time::seconds(5));
63 timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
68 if (num_replies_ == 0)
69 std::cout << "Request timed out" << std::endl;
71 // Requests must be sent no less than one second apart.
72 timer_.expires_at(time_sent_ + posix_time::seconds(1));
73 timer_.async_wait(boost::bind(&pinger::start_send, this));
78 // Discard any data already in the buffer.
79 reply_buffer_.consume(reply_buffer_.size());
81 // Wait for a reply. We prepare the buffer to receive up to 64KB.
82 socket_.async_receive(reply_buffer_.prepare(65536),
83 boost::bind(&pinger::handle_receive, this, _2));
86 void handle_receive(std::size_t length)
88 // The actual number of bytes received is committed to the buffer so that we
89 // can extract it using a std::istream object.
90 reply_buffer_.commit(length);
92 // Decode the reply packet.
93 std::istream is(&reply_buffer_);
96 is >> ipv4_hdr >> icmp_hdr;
98 // We can receive all ICMP packets received by the host, so we need to
99 // filter out only the echo replies that match the our identifier and
100 // expected sequence number.
101 if (is && icmp_hdr.type() == icmp_header::echo_reply
102 && icmp_hdr.identifier() == get_identifier()
103 && icmp_hdr.sequence_number() == sequence_number_)
105 // If this is the first reply, interrupt the five second timeout.
106 if (num_replies_++ == 0)
109 // Print out some information about the reply packet.
110 posix_time::ptime now = posix_time::microsec_clock::universal_time();
111 std::cout << length - ipv4_hdr.header_length()
112 << " bytes from " << ipv4_hdr.source_address()
113 << ": icmp_seq=" << icmp_hdr.sequence_number()
114 << ", ttl=" << ipv4_hdr.time_to_live()
115 << ", time=" << (now - time_sent_).total_milliseconds() << " ms"
122 static unsigned short get_identifier()
124 #if defined(BOOST_ASIO_WINDOWS)
125 return static_cast<unsigned short>(::GetCurrentProcessId());
127 return static_cast<unsigned short>(::getpid());
131 icmp::resolver resolver_;
132 icmp::endpoint destination_;
133 icmp::socket socket_;
134 deadline_timer timer_;
135 unsigned short sequence_number_;
136 posix_time::ptime time_sent_;
137 boost::asio::streambuf reply_buffer_;
138 std::size_t num_replies_;
141 int main(int argc, char* argv[])
147 std::cerr << "Usage: ping <host>" << std::endl;
148 #if !defined(BOOST_ASIO_WINDOWS)
149 std::cerr << "(You may need to run this program as root.)" << std::endl;
154 boost::asio::io_service io_service;
155 pinger p(io_service, argv[1]);
158 catch (std::exception& e)
160 std::cerr << "Exception: " << e.what() << std::endl;