Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r54767 - in trunk/libs/asio: doc doc/overview example/icmp
From: chris_at_[hidden]
Date: 2009-07-07 08:37:16


Author: chris_kohlhoff
Date: 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
New Revision: 54767
URL: http://svn.boost.org/trac/boost/changeset/54767

Log:
Add ping example.

Added:
   trunk/libs/asio/example/icmp/
   trunk/libs/asio/example/icmp/Jamfile (contents, props changed)
   trunk/libs/asio/example/icmp/Jamfile.v2 (contents, props changed)
   trunk/libs/asio/example/icmp/icmp_header.hpp (contents, props changed)
   trunk/libs/asio/example/icmp/ipv4_header.hpp (contents, props changed)
   trunk/libs/asio/example/icmp/ping.cpp (contents, props changed)
Text files modified:
   trunk/libs/asio/doc/Jamfile.v2 | 5 +++--
   trunk/libs/asio/doc/examples.qbk | 9 +++++++++
   trunk/libs/asio/doc/overview/protocols.qbk | 11 ++++-------
   3 files changed, 16 insertions(+), 9 deletions(-)

Modified: trunk/libs/asio/doc/Jamfile.v2
==============================================================================
--- trunk/libs/asio/doc/Jamfile.v2 (original)
+++ trunk/libs/asio/doc/Jamfile.v2 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -33,8 +33,9 @@
   ;
 
 local example-names = allocation buffers chat echo http/client http/server
- http/server2 http/server3 invocation iostreams local multicast nonblocking
- porthopper serialization services socks4 ssl timeouts timers windows ;
+ http/server2 http/server3 icmp invocation iostreams local multicast
+ nonblocking porthopper serialization services socks4 ssl timeouts timers
+ windows ;
 
 for local l in $(example-names)
 {

Modified: trunk/libs/asio/doc/examples.qbk
==============================================================================
--- trunk/libs/asio/doc/examples.qbk (original)
+++ trunk/libs/asio/doc/examples.qbk 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -135,6 +135,15 @@
 * [@boost_asio/example/http/server3/win_main.cpp]
 
 
+[heading ICMP]
+
+This example shows how to use raw sockets with ICMP to ping a remote host.
+
+* [@boost_asio/example/icmp/ping.cpp]
+* [@boost_asio/example/icmp/ipv4_header.hpp]
+* [@boost_asio/example/icmp/icmp_header.hpp]
+
+
 [heading Invocation]
 
 This example shows how to customise handler invocation. Completion handlers are

Modified: trunk/libs/asio/doc/overview/protocols.qbk
==============================================================================
--- trunk/libs/asio/doc/overview/protocols.qbk (original)
+++ trunk/libs/asio/doc/overview/protocols.qbk 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -89,7 +89,7 @@
 As with TCP and UDP, ICMP hostname resolution is performed using a resolver:
 
   ip::icmp::resolver resolver(my_io_service);
- ip::icmp::resolver::query query("localhost", "daytime");
+ ip::icmp::resolver::query query("localhost", "");
   ip::icmp::resolver::iterator iter = resolver.resolve(query);
   ...
 
@@ -106,11 +106,7 @@
 boost_asio.reference.basic_raw_socket.async_receive_from async_receive_from()],
 [link boost_asio.reference.basic_raw_socket.send_to send_to()] or [link
 boost_asio.reference.basic_raw_socket.async_send_to async_send_to()] member
-functions. For a connected ICMP socket, use the [link
-boost_asio.reference.basic_raw_socket.receive receive()], [link
-boost_asio.reference.basic_raw_socket.async_receive async_receive()], [link
-boost_asio.reference.basic_raw_socket.send send()] or [link
-boost_asio.reference.basic_raw_socket.async_send async_send()] member functions.
+functions.
 
 [heading Other Protocols]
 
@@ -123,6 +119,7 @@
 [link boost_asio.reference.ip__tcp ip::tcp],
 [link boost_asio.reference.ip__udp ip::udp],
 [link boost_asio.reference.ip__icmp ip::icmp],
-[link boost_asio.tutorial.tutdaytime1 daytime protocol tutorials].
+[link boost_asio.tutorial.tutdaytime1 daytime protocol tutorials],
+[link boost_asio.examples.icmp ICMP ping example].
 
 [endsect]

Added: trunk/libs/asio/example/icmp/Jamfile
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/icmp/Jamfile 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -0,0 +1,33 @@
+#
+# Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+
+subproject libs/asio/example/icmp ;
+
+project boost : $(BOOST_ROOT) ;
+
+if $(UNIX)
+{
+ switch $(JAMUNAME)
+ {
+ case SunOS* :
+ {
+ SOCKET_LIBS = <find-library>socket <find-library>nsl ;
+ }
+ }
+}
+
+exe ping
+ : <lib>@boost/libs/system/build/boost_system
+ ping.cpp
+ : <include>$(BOOST_ROOT)
+ <include>../../../..
+ <define>BOOST_ALL_NO_LIB=1
+ <threading>multi
+ <mingw><*><find-library>ws2_32
+ <mingw><*><find-library>mswsock
+ $(SOCKET_LIBS)
+ ;

Added: trunk/libs/asio/example/icmp/Jamfile.v2
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/icmp/Jamfile.v2 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+#
+# Distributed under the Boost Software License, Version 1.0. (See accompanying
+# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+#
+
+import os ;
+
+if [ os.name ] = SOLARIS
+{
+ lib socket ;
+ lib nsl ;
+}
+else if [ os.name ] = NT
+{
+ lib ws2_32 ;
+ lib mswsock ;
+}
+else if [ os.name ] = HPUX
+{
+ lib ipv6 ;
+}
+
+exe ping
+ : ping.cpp
+ /boost/system//boost_system
+ : <define>BOOST_ALL_NO_LIB=1
+ <threading>multi
+ <os>SOLARIS:<library>socket
+ <os>SOLARIS:<library>nsl
+ <os>NT:<define>_WIN32_WINNT=0x0501
+ <os>NT,<toolset>gcc:<library>ws2_32
+ <os>NT,<toolset>gcc:<library>mswsock
+ <os>NT,<toolset>gcc-cygwin:<define>__USE_W32_SOCKETS
+ <os>HPUX,<toolset>gcc:<define>_XOPEN_SOURCE_EXTENDED
+ <os>HPUX:<library>ipv6
+ ;

Added: trunk/libs/asio/example/icmp/icmp_header.hpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/icmp/icmp_header.hpp 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -0,0 +1,94 @@
+//
+// icmp_header.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef ICMP_HEADER_HPP
+#define ICMP_HEADER_HPP
+
+#include <istream>
+#include <ostream>
+#include <algorithm>
+
+// ICMP header for both IPv4 and IPv6.
+//
+// The wire format of an ICMP header is:
+//
+// 0 8 16 31
+// +---------------+---------------+------------------------------+ ---
+// | | | | ^
+// | type | code | checksum | |
+// | | | | |
+// +---------------+---------------+------------------------------+ 8 bytes
+// | | | |
+// | identifier | sequence number | |
+// | | | v
+// +-------------------------------+------------------------------+ ---
+
+class icmp_header
+{
+public:
+ enum { echo_reply = 0, destination_unreachable = 3, source_quench = 4,
+ redirect = 5, echo_request = 8, time_exceeded = 11, parameter_problem = 12,
+ timestamp_request = 13, timestamp_reply = 14, info_request = 15,
+ info_reply = 16, address_request = 17, address_reply = 18 };
+
+ icmp_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
+
+ unsigned char type() const { return rep_[0]; }
+ unsigned char code() const { return rep_[1]; }
+ unsigned short checksum() const { return decode(2, 3); }
+ unsigned short identifier() const { return decode(4, 5); }
+ unsigned short sequence_number() const { return decode(6, 7); }
+
+ void type(unsigned char n) { rep_[0] = n; }
+ void code(unsigned char n) { rep_[1] = n; }
+ void checksum(unsigned short n) { encode(2, 3, n); }
+ void identifier(unsigned short n) { encode(4, 5, n); }
+ void sequence_number(unsigned short n) { encode(6, 7, n); }
+
+ friend std::istream& operator>>(std::istream& is, icmp_header& header)
+ { return is.read(reinterpret_cast<char*>(header.rep_), 8); }
+
+ friend std::ostream& operator<<(std::ostream& os, const icmp_header& header)
+ { return os.write(reinterpret_cast<const char*>(header.rep_), 8); }
+
+private:
+ unsigned short decode(int a, int b) const
+ { return (rep_[a] << 8) + rep_[b]; }
+
+ void encode(int a, int b, unsigned short n)
+ {
+ rep_[a] = static_cast<unsigned char>(n >> 8);
+ rep_[b] = static_cast<unsigned char>(n & 0xFF);
+ }
+
+ unsigned char rep_[8];
+};
+
+template <typename Iterator>
+void compute_checksum(icmp_header& header,
+ Iterator body_begin, Iterator body_end)
+{
+ unsigned int sum = (header.type() << 8) + header.code()
+ + header.identifier() + header.sequence_number();
+
+ Iterator body_iter = body_begin;
+ while (body_iter != body_end)
+ {
+ sum += (static_cast<unsigned char>(*body_iter++) << 8);
+ if (body_iter != body_end)
+ sum += static_cast<unsigned char>(*body_iter++);
+ }
+
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+ header.checksum(static_cast<unsigned short>(~sum));
+}
+
+#endif // ICMP_HEADER_HPP

Added: trunk/libs/asio/example/icmp/ipv4_header.hpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/icmp/ipv4_header.hpp 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -0,0 +1,102 @@
+//
+// ipv4_header.hpp
+// ~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#ifndef IPV4_HEADER_HPP
+#define IPV4_HEADER_HPP
+
+#include <algorithm>
+#include <boost/asio/ip/address_v4.hpp>
+
+// Packet header for IPv4.
+//
+// The wire format of an IPv4 header is:
+//
+// 0 8 16 31
+// +-------+-------+---------------+------------------------------+ ---
+// | | | | | ^
+// |version|header | type of | total length in bytes | |
+// | (4) | length| service | | |
+// +-------+-------+---------------+-+-+-+------------------------+ |
+// | | | | | | |
+// | identification |0|D|M| fragment offset | |
+// | | |F|F| | |
+// +---------------+---------------+-+-+-+------------------------+ |
+// | | | | |
+// | time to live | protocol | header checksum | 20 bytes
+// | | | | |
+// +---------------+---------------+------------------------------+ |
+// | | |
+// | source IPv4 address | |
+// | | |
+// +--------------------------------------------------------------+ |
+// | | |
+// | destination IPv4 address | |
+// | | v
+// +--------------------------------------------------------------+ ---
+// | | ^
+// | | |
+// / options (if any) / 0 - 40
+// / / bytes
+// | | |
+// | | v
+// +--------------------------------------------------------------+ ---
+
+class ipv4_header
+{
+public:
+ ipv4_header() { std::fill(rep_, rep_ + sizeof(rep_), 0); }
+
+ unsigned char version() const { return (rep_[0] >> 4) & 0xF; }
+ unsigned short header_length() const { return (rep_[0] & 0xF) * 4; }
+ unsigned char type_of_service() const { return rep_[1]; }
+ unsigned short total_length() const { return decode(2, 3); }
+ unsigned short identification() const { return decode(4, 5); }
+ bool dont_fragment() const { return (rep_[6] & 0x40) != 0; }
+ bool more_fragments() const { return (rep_[6] & 0x20) != 0; }
+ unsigned short fragment_offset() const { return decode(6, 7) & 0x1FFF; }
+ unsigned int time_to_live() const { return rep_[8]; }
+ unsigned char protocol() const { return rep_[9]; }
+ unsigned short header_checksum() const { return decode(10, 11); }
+
+ boost::asio::ip::address_v4 source_address() const
+ {
+ boost::asio::ip::address_v4::bytes_type bytes
+ = { { rep_[12], rep_[13], rep_[14], rep_[15] } };
+ return boost::asio::ip::address_v4(bytes);
+ }
+
+ boost::asio::ip::address_v4 destination_address() const
+ {
+ boost::asio::ip::address_v4::bytes_type bytes
+ = { { rep_[16], rep_[17], rep_[18], rep_[19] } };
+ return boost::asio::ip::address_v4(bytes);
+ }
+
+ friend std::istream& operator>>(std::istream& is, ipv4_header& header)
+ {
+ is.read(reinterpret_cast<char*>(header.rep_), 20);
+ if (header.version() != 4)
+ is.setstate(std::ios::failbit);
+ std::streamsize options_length = header.header_length() - 20;
+ if (options_length < 0 || options_length > 40)
+ is.setstate(std::ios::failbit);
+ else
+ is.read(reinterpret_cast<char*>(header.rep_) + 20, options_length);
+ return is;
+ }
+
+private:
+ unsigned short decode(int a, int b) const
+ { return (rep_[a] << 8) + rep_[b]; }
+
+ unsigned char rep_[60];
+};
+
+#endif // IPV4_HEADER_HPP

Added: trunk/libs/asio/example/icmp/ping.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/icmp/ping.cpp 2009-07-07 08:37:15 EDT (Tue, 07 Jul 2009)
@@ -0,0 +1,162 @@
+//
+// ping.cpp
+// ~~~~~~~~
+//
+// Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com)
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <istream>
+#include <iostream>
+#include <ostream>
+
+#include "icmp_header.hpp"
+#include "ipv4_header.hpp"
+
+using boost::asio::ip::icmp;
+using boost::asio::deadline_timer;
+namespace posix_time = boost::posix_time;
+
+class pinger
+{
+public:
+ pinger(boost::asio::io_service& io_service, const char* destination)
+ : resolver_(io_service), socket_(io_service, icmp::v4()),
+ timer_(io_service), sequence_number_(0), num_replies_(0)
+ {
+ icmp::resolver::query query(icmp::v4(), destination, "");
+ destination_ = *resolver_.resolve(query);
+
+ start_send();
+ start_receive();
+ }
+
+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();
+ socket_.send_to(request_buffer.data(), destination_);
+
+ // Wait up to five seconds for a reply.
+ num_replies_ = 0;
+ timer_.expires_at(time_sent_ + posix_time::seconds(5));
+ timer_.async_wait(boost::bind(&pinger::handle_timeout, this));
+ }
+
+ void handle_timeout()
+ {
+ if (num_replies_ == 0)
+ std::cout << "Request timed out" << std::endl;
+
+ // Requests must be sent no less than one second apart.
+ timer_.expires_at(time_sent_ + posix_time::seconds(1));
+ timer_.async_wait(boost::bind(&pinger::start_send, this));
+ }
+
+ 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),
+ boost::bind(&pinger::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();
+
+ // Print out some information about the reply packet.
+ posix_time::ptime now = posix_time::microsec_clock::universal_time();
+ std::cout << length - ipv4_hdr.header_length()
+ << " bytes from " << ipv4_hdr.source_address()
+ << ": icmp_seq=" << icmp_hdr.sequence_number()
+ << ", ttl=" << ipv4_hdr.time_to_live()
+ << ", time=" << (now - time_sent_).total_milliseconds() << " ms"
+ << std::endl;
+ }
+
+ start_receive();
+ }
+
+ static unsigned short get_identifier()
+ {
+#if defined(BOOST_WINDOWS)
+ return static_cast<unsigned short>(::GetCurrentProcessId());
+#else
+ return static_cast<unsigned short>(::getpid());
+#endif
+ }
+
+ icmp::resolver resolver_;
+ icmp::endpoint destination_;
+ icmp::socket socket_;
+ deadline_timer timer_;
+ unsigned short sequence_number_;
+ posix_time::ptime time_sent_;
+ boost::asio::streambuf reply_buffer_;
+ std::size_t num_replies_;
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 2)
+ {
+ std::cerr << "Usage: ping <host>" << std::endl;
+#if !defined(BOOST_WINDOWS)
+ std::cerr << "(You may need to run this program as root.)" << std::endl;
+#endif
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+ pinger p(io_service, argv[1]);
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << std::endl;
+ }
+}


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk