Imported Upstream version 1.57.0
[platform/upstream/boost.git] / doc / html / boost_asio / example / cpp03 / icmp / ping.cpp
1 //
2 // ping.cpp
3 // ~~~~~~~~
4 //
5 // Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
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)
9 //
10
11 #include <boost/asio.hpp>
12 #include <boost/bind.hpp>
13 #include <istream>
14 #include <iostream>
15 #include <ostream>
16
17 #include "icmp_header.hpp"
18 #include "ipv4_header.hpp"
19
20 using boost::asio::ip::icmp;
21 using boost::asio::deadline_timer;
22 namespace posix_time = boost::posix_time;
23
24 class pinger
25 {
26 public:
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)
30   {
31     icmp::resolver::query query(icmp::v4(), destination, "");
32     destination_ = *resolver_.resolve(query);
33
34     start_send();
35     start_receive();
36   }
37
38 private:
39   void start_send()
40   {
41     std::string body("\"Hello!\" from Asio ping.");
42
43     // Create an ICMP header for an echo request.
44     icmp_header echo_request;
45     echo_request.type(icmp_header::echo_request);
46     echo_request.code(0);
47     echo_request.identifier(get_identifier());
48     echo_request.sequence_number(++sequence_number_);
49     compute_checksum(echo_request, body.begin(), body.end());
50
51     // Encode the request packet.
52     boost::asio::streambuf request_buffer;
53     std::ostream os(&request_buffer);
54     os << echo_request << body;
55
56     // Send the request.
57     time_sent_ = posix_time::microsec_clock::universal_time();
58     socket_.send_to(request_buffer.data(), destination_);
59
60     // Wait up to five seconds for a reply.
61     num_replies_ = 0;
62     timer_.expires_at(time_sent_ + posix_time::seconds(5));
63     timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
64   }
65
66   void handle_timeout()
67   {
68     if (num_replies_ == 0)
69       std::cout << "Request timed out" << std::endl;
70
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));
74   }
75
76   void start_receive()
77   {
78     // Discard any data already in the buffer.
79     reply_buffer_.consume(reply_buffer_.size());
80
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));
84   }
85
86   void handle_receive(std::size_t length)
87   {
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);
91
92     // Decode the reply packet.
93     std::istream is(&reply_buffer_);
94     ipv4_header ipv4_hdr;
95     icmp_header icmp_hdr;
96     is >> ipv4_hdr >> icmp_hdr;
97
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_)
104     {
105       // If this is the first reply, interrupt the five second timeout.
106       if (num_replies_++ == 0)
107         timer_.cancel();
108
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"
116         << std::endl;
117     }
118
119     start_receive();
120   }
121
122   static unsigned short get_identifier()
123   {
124 #if defined(BOOST_ASIO_WINDOWS)
125     return static_cast<unsigned short>(::GetCurrentProcessId());
126 #else
127     return static_cast<unsigned short>(::getpid());
128 #endif
129   }
130
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_;
139 };
140
141 int main(int argc, char* argv[])
142 {
143   try
144   {
145     if (argc != 2)
146     {
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;
150 #endif
151       return 1;
152     }
153
154     boost::asio::io_service io_service;
155     pinger p(io_service, argv[1]);
156     io_service.run();
157   }
158   catch (std::exception& e)
159   {
160     std::cerr << "Exception: " << e.what() << std::endl;
161   }
162 }