src/examples/cpp03/icmp/ping.cpp | src/examples/cpp11/icmp/ping.cpp |
⋮ | ⋮ |
1 | // | 1 | // |
2 | //·ping.cpp | 2 | //·ping.cpp |
3 | //·~~~~~~~~ | 3 | //·~~~~~~~~ |
4 | // | 4 | // |
5 | //·Copyright·(c)·2003-2018·Christopher·M.·Kohlhoff·(chris·at·kohlhoff·dot·com) | 5 | //·Copyright·(c)·2003-2018·Christopher·M.·Kohlhoff·(chris·at·kohlhoff·dot·com) |
6 | // | 6 | // |
7 | //·Distributed·under·the·Boost·Software·License,·Version·1.0.·(See·accompanying | 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) | 8 | //·file·LICENSE_1_0.txt·or·copy·at·http://www.boost.org/LICENSE_1_0.txt) |
9 | // | 9 | // |
10 | | 10 | |
| 11 | #include·<array> |
11 | #include·<asio.hpp> | 12 | #include·<asio.hpp> |
12 | #include·<boost/bind.hpp> | 13 | #include·<chrono> |
| 14 | #include·<functional> |
13 | #include·<istream> | 15 | #include·<istream> |
14 | #include·<iostream> | 16 | #include·<iostream> |
15 | #include·<ostream> | 17 | #include·<ostream> |
16 | | 18 | |
17 | #include·"icmp_header.hpp" | 19 | #include·"icmp_header.hpp" |
18 | #include·"ipv4_header.hpp" | 20 | #include·"ipv4_header.hpp" |
19 | | 21 | |
20 | using·asio::ip::icmp; | 22 | using·asio::ip::icmp; |
21 | using·asio::steady_timer; | 23 | using·asio::steady_timer; |
22 | namespace·chrono·=·asio::chrono; | |
23 | | 24 | |
24 | class·pinger | 25 | class·pinger |
25 | { | 26 | { |
26 | public: | 27 | public: |
27 | ··pinger(asio::io_context&·io_context,·const·char*·destination) | 28 | ··pinger(asio::io_context&·io_context,·const·char*·destination) |
28 | ····:·resolver_(io_context),·socket_(io_context,·icmp::v4()), | 29 | ····:·resolver_(io_context),·socket_(io_context,·icmp::v4()), |
29 | ······timer_(io_context),·sequence_number_(0),·num_replies_(0) | 30 | ······timer_(io_context),·sequence_number_(0),·num_replies_(0) |
30 | ··{ | 31 | ··{ |
31 | ····destination_·=·*resolver_.resolve(icmp::v4(),·destination,·"").begin(); | 32 | ····destination_·=·*resolver_.resolve(icmp::v4(),·destination,·"").begin(); |
32 | | 33 | |
33 | ····start_send(); | 34 | ····start_send(); |
34 | ····start_receive(); | 35 | ····start_receive(); |
35 | ··} | 36 | ··} |
36 | | 37 | |
37 | private: | 38 | private: |
38 | ··void·start_send() | 39 | ··void·start_send() |
39 | ··{ | 40 | ··{ |
40 | ····std::string·body("\"Hello!\"·from·Asio·ping."); | 41 | ····std::string·body("\"Hello!\"·from·Asio·ping."); |
41 | | 42 | |
42 | ····//·Create·an·ICMP·header·for·an·echo·request. | 43 | ····//·Create·an·ICMP·header·for·an·echo·request. |
43 | ····icmp_header·echo_request; | 44 | ····icmp_header·echo_request; |
44 | ····echo_request.type(icmp_header::echo_request); | 45 | ····echo_request.type(icmp_header::echo_request); |
45 | ····echo_request.code(0); | 46 | ····echo_request.code(0); |
46 | ····echo_request.identifier(get_identifier()); | 47 | ····echo_request.identifier(get_identifier()); |
47 | ····echo_request.sequence_number(++sequence_number_); | 48 | ····echo_request.sequence_number(++sequence_number_); |
48 | ····compute_checksum(echo_request,·body.begin(),·body.end()); | 49 | ····compute_checksum(echo_request,·body.begin(),·body.end()); |
49 | | 50 | |
50 | ····//·Encode·the·request·packet. | 51 | ····//·Assemble·the·request·packet. |
51 | ····asio::streambuf·request_buffer; | 52 | ····std::array<asio::const_buffer,·2>·request_buffers·=·{{ |
52 | ····std::ostream·os(&request_buffer); | 53 | ······asio::buffer(&echo_request,·sizeof(echo_request)), |
53 | ····os·<<·echo_request·<<·body; | 54 | ······asio::buffer(body) |
| 55 | ····}}; |
54 | | 56 | |
55 | ····//·Send·the·request. | 57 | ····//·Send·the·request. |
56 | ····time_sent_·=·steady_timer::clock_type::now(); | 58 | ····time_sent_·=·steady_timer::clock_type::now(); |
57 | ····socket_.send_to(request_buffer.data(),·destination_); | 59 | ····socket_.send_to(request_buffers,·destination_); |
58 | | 60 | |
59 | ····//·Wait·up·to·five·seconds·for·a·reply. | 61 | ····//·Wait·up·to·five·seconds·for·a·reply. |
60 | ····num_replies_·=·0; | 62 | ····num_replies_·=·0; |
61 | ····timer_.expires_at(time_sent_·+·chrono::seconds(5)); | 63 | ····timer_.expires_at(time_sent_·+·std::chrono::seconds(5)); |
62 | ····timer_.async_wait(boost::bind(&pinger::handle_timeout,·this)); | 64 | ····timer_.async_wait(std::bind(&pinger::handle_timeout,·this)); |
63 | ··} | 65 | ··} |
64 | | 66 | |
65 | ··void·handle_timeout() | 67 | ··void·handle_timeout() |
66 | ··{ | 68 | ··{ |
67 | ····if·(num_replies_·==·0) | 69 | ····if·(num_replies_·==·0) |
68 | ······std::cout·<<·"Request·timed·out"·<<·std::endl; | 70 | ······std::cout·<<·"Request·timed·out"·<<·std::endl; |
69 | | 71 | |
70 | ····//·Requests·must·be·sent·no·less·than·one·second·apart. | 72 | ····//·Requests·must·be·sent·no·less·than·one·second·apart. |
71 | ····timer_.expires_at(time_sent_·+·chrono::seconds(1)); | 73 | ····timer_.expires_at(time_sent_·+·std::chrono::seconds(1)); |
72 | ····timer_.async_wait(boost::bind(&pinger::start_send,·this)); | 74 | ····timer_.async_wait(std::bind(&pinger::start_send,·this)); |
73 | ··} | 75 | ··} |
74 | | 76 | |
75 | ··void·start_receive() | 77 | ··void·start_receive() |
76 | ··{ | 78 | ··{ |
77 | ····//·Discard·any·data·already·in·the·buffer. | 79 | ····//·Discard·any·data·already·in·the·buffer. |
78 | ····reply_buffer_.consume(reply_buffer_.size()); | 80 | ····reply_buffer_.consume(reply_buffer_.size()); |
79 | | 81 | |
80 | ····//·Wait·for·a·reply.·We·prepare·the·buffer·to·receive·up·to·64KB. | 82 | ····//·Wait·for·a·reply.·We·prepare·the·buffer·to·receive·up·to·64KB. |
81 | ····socket_.async_receive(reply_buffer_.prepare(65536), | 83 | ····socket_.async_receive(reply_buffer_.prepare(65536), |
82 | ········boost::bind(&pinger::handle_receive,·this,·_2)); | 84 | ········boost::bind(&pinger::handle_receive,·this,·_2)); |
83 | ··} | 85 | ··} |
84 | | 86 | |
85 | ··void·handle_receive(std::size_t·length) | 87 | ··void·handle_receive(std::size_t·length) |
86 | ··{ | 88 | ··{ |
87 | ····//·The·actual·number·of·bytes·received·is·committed·to·the·buffer·so·that·we | 89 | ····//·The·actual·number·of·bytes·received·is·committed·to·the·buffer·so·that·we |
88 | ····//·can·extract·it·using·a·std::istream·object. | 90 | ····//·can·extract·it·using·a·std::istream·object. |
89 | ····reply_buffer_.commit(length); | 91 | ····reply_buffer_.commit(length); |
90 | | 92 | |
91 | ····//·Decode·the·reply·packet. | 93 | ····//·Decode·the·reply·packet. |
92 | ····std::istream·is(&reply_buffer_); | 94 | ····std::istream·is(&reply_buffer_); |
93 | ····ipv4_header·ipv4_hdr; | 95 | ····ipv4_header·ipv4_hdr; |
94 | ····icmp_header·icmp_hdr; | 96 | ····icmp_header·icmp_hdr; |
95 | ····is·>>·ipv4_hdr·>>·icmp_hdr; | 97 | ····is·>>·ipv4_hdr·>>·icmp_hdr; |
96 | | 98 | |
97 | ····//·We·can·receive·all·ICMP·packets·received·by·the·host,·so·we·need·to | 99 | ····//·We·can·receive·all·ICMP·packets·received·by·the·host,·so·we·need·to |
98 | ····//·filter·out·only·the·echo·replies·that·match·the·our·identifier·and | 100 | ····//·filter·out·only·the·echo·replies·that·match·the·our·identifier·and |
99 | ····//·expected·sequence·number. | 101 | ····//·expected·sequence·number. |
100 | ····if·(is·&&·icmp_hdr.type()·==·icmp_header::echo_reply | 102 | ····if·(is·&&·icmp_hdr.type()·==·icmp_header::echo_reply |
101 | ··········&&·icmp_hdr.identifier()·==·get_identifier() | 103 | ··········&&·icmp_hdr.identifier()·==·get_identifier() |
102 | ··········&&·icmp_hdr.sequence_number()·==·sequence_number_) | 104 | ··········&&·icmp_hdr.sequence_number()·==·sequence_number_) |
103 | ····{ | 105 | ····{ |
104 | ······//·If·this·is·the·first·reply,·interrupt·the·five·second·timeout. | 106 | ······//·If·this·is·the·first·reply,·interrupt·the·five·second·timeout. |
105 | ······if·(num_replies_++·==·0) | 107 | ······if·(num_replies_++·==·0) |
106 | ········timer_.cancel(); | 108 | ········timer_.cancel(); |
107 | | 109 | |
108 | ······//·Print·out·some·information·about·the·reply·packet. | 110 | ······//·Print·out·some·information·about·the·reply·packet. |
109 | ······chrono::steady_clock::time_point·now·=·chrono::steady_clock::now(); | 111 | ······auto·now·=·steady_timer::clock_type::now(); |
110 | ······chrono::steady_clock::duration·elapsed·=·now·-·time_sent_; | |
111 | ······std::cout·<<·length·-·ipv4_hdr.header_length() | 112 | ······std::cout·<<·length·-·ipv4_hdr.header_length() |
112 | ········<<·"·bytes·from·"·<<·ipv4_hdr.source_address() | 113 | ········<<·"·bytes·from·"·<<·ipv4_hdr.source_address() |
113 | ········<<·":·icmp_seq="·<<·icmp_hdr.sequence_number() | 114 | ········<<·":·icmp_seq="·<<·icmp_hdr.sequence_number() |
114 | ········<<·",·ttl="·<<·ipv4_hdr.time_to_live() | 115 | ········<<·",·ttl="·<<·ipv4_hdr.time_to_live() |
115 | ········<<·",·time=" | 116 | ········<<·",·time="·<<·(now·-·time_sent_).total_milliseconds()·<<·"·ms" |
116 | ········<<·chrono::duration_cast<chrono::milliseconds>(elapsed).count() | |
117 | ········<<·std::endl; | 117 | ········<<·std::endl; |
118 | ····} | 118 | ····} |
119 | | 119 | |
120 | ····start_receive(); | 120 | ····start_receive(); |
121 | ··} | 121 | ··} |
122 | | 122 | |
123 | ··static·unsigned·short·get_identifier() | 123 | ··static·unsigned·short·get_identifier() |
124 | ··{ | 124 | ··{ |
125 | #if·defined(ASIO_WINDOWS) | 125 | #if·defined(ASIO_WINDOWS) |
126 | ····return·static_cast<unsigned·short>(::GetCurrentProcessId()); | 126 | ····return·static_cast<unsigned·short>(::GetCurrentProcessId()); |
127 | #else | 127 | #else |
128 | ····return·static_cast<unsigned·short>(::getpid()); | 128 | ····return·static_cast<unsigned·short>(::getpid()); |
129 | #endif | 129 | #endif |
130 | ··} | 130 | ··} |
131 | | 131 | |
132 | ··icmp::resolver·resolver_; | 132 | ··icmp::resolver·resolver_; |
133 | ··icmp::endpoint·destination_; | 133 | ··icmp::endpoint·destination_; |
134 | ··icmp::socket·socket_; | 134 | ··icmp::socket·socket_; |
135 | ··steady_timer·timer_; | 135 | ··steady_timer·timer_; |
136 | ··unsigned·short·sequence_number_; | 136 | ··unsigned·short·sequence_number_; |
137 | ··chrono::steady_clock::time_point·time_sent_; | 137 | ··steady_timer::clock_type::time_point·time_sent_; |
138 | ··asio::streambuf·reply_buffer_; | 138 | ··std::string·reply_buffer_; |
139 | ··std::size_t·num_replies_; | 139 | ··std::size_t·num_replies_; |
140 | }; | 140 | }; |
141 | | 141 | |
142 | int·main(int·argc,·char*·argv[]) | 142 | int·main(int·argc,·char*·argv[]) |
143 | { | 143 | { |
144 | ··try | 144 | ··try |
145 | ··{ | 145 | ··{ |
146 | ····if·(argc·!=·2) | 146 | ····if·(argc·!=·2) |
147 | ····{ | 147 | ····{ |
148 | ······std::cerr·<<·"Usage:·ping·<host>"·<<·std::endl; | 148 | ······std::cerr·<<·"Usage:·ping·<host>"·<<·std::endl; |
149 | #if·!defined(ASIO_WINDOWS) | 149 | #if·!defined(ASIO_WINDOWS) |
150 | ······std::cerr·<<·"(You·may·need·to·run·this·program·as·root.)"·<<·std::endl; | 150 | ······std::cerr·<<·"(You·may·need·to·run·this·program·as·root.)"·<<·std::endl; |
151 | #endif | 151 | #endif |
152 | ······return·1; | 152 | ······return·1; |
153 | ····} | 153 | ····} |
154 | | 154 | |
155 | ····asio::io_context·io_context; | 155 | ····asio::io_context·io_context; |
156 | ····pinger·p(io_context,·argv[1]); | 156 | ····pinger·p(io_context,·argv[1]); |
157 | ····io_context.run(); | 157 | ····io_context.run(); |
158 | ··} | 158 | ··} |
159 | ··catch·(std::exception&·e) | 159 | ··catch·(std::exception&·e) |
160 | ··{ | 160 | ··{ |
161 | ····std::cerr·<<·"Exception:·"·<<·e.what()·<<·std::endl; | 161 | ····std::cerr·<<·"Exception:·"·<<·e.what()·<<·std::endl; |
162 | ··} | 162 | ··} |
163 | } | 163 | } |