|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r63569 - trunk/libs/asio/example/timeouts
From: chris_at_[hidden]
Date: 2010-07-04 02:53:59
Author: chris_kohlhoff
Date: 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
New Revision: 63569
URL: http://svn.boost.org/trac/boost/changeset/63569
Log:
Reworked timeout examples.
Added:
trunk/libs/asio/example/timeouts/async_tcp_client.cpp (contents, props changed)
trunk/libs/asio/example/timeouts/blocking_tcp_client.cpp (contents, props changed)
trunk/libs/asio/example/timeouts/blocking_udp_client.cpp (contents, props changed)
trunk/libs/asio/example/timeouts/server.cpp (contents, props changed)
Removed:
trunk/libs/asio/example/timeouts/accept_timeout.cpp
trunk/libs/asio/example/timeouts/connect_timeout.cpp
trunk/libs/asio/example/timeouts/datagram_receive_timeout.cpp
trunk/libs/asio/example/timeouts/stream_receive_timeout.cpp
Text files modified:
trunk/libs/asio/example/timeouts/Jamfile | 16 ++++++++--------
trunk/libs/asio/example/timeouts/Jamfile.v2 | 8 ++++----
2 files changed, 12 insertions(+), 12 deletions(-)
Modified: trunk/libs/asio/example/timeouts/Jamfile
==============================================================================
--- trunk/libs/asio/example/timeouts/Jamfile (original)
+++ trunk/libs/asio/example/timeouts/Jamfile 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -32,22 +32,22 @@
$(SOCKET_LIBS)
;
-exe accept_timeout
+exe async_tcp_client
: <template>asio_timeouts_example
- accept_timeout.cpp
+ async_tcp_client.cpp
;
-exe connect_timeout
+exe blocking_tcp_client
: <template>asio_timeouts_example
- connect_timeout.cpp
+ blocking_tcp_client.cpp
;
-exe datagram_receive_timeout
+exe blocking_udp_client
: <template>asio_timeouts_example
- datagram_receive_timeout.cpp
+ blocking_udp_client.cpp
;
-exe stream_receive_timeout
+exe server
: <template>asio_timeouts_example
- stream_receive_timeout.cpp
+ server.cpp
;
Modified: trunk/libs/asio/example/timeouts/Jamfile.v2
==============================================================================
--- trunk/libs/asio/example/timeouts/Jamfile.v2 (original)
+++ trunk/libs/asio/example/timeouts/Jamfile.v2 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -37,7 +37,7 @@
<os>HPUX:<library>ipv6
;
-exe accept_timeout : accept_timeout.cpp ;
-exe connect_timeout : connect_timeout.cpp ;
-exe datagram_receive_timeout : datagram_receive_timeout.cpp ;
-exe stream_receive_timeout : stream_receive_timeout.cpp ;
+exe async_tcp_client : async_tcp_client.cpp ;
+exe blocking_tcp_client : blocking_tcp_client.cpp ;
+exe blocking_udp_client : blocking_udp_client.cpp ;
+exe server : server.cpp ;
Deleted: trunk/libs/asio/example/timeouts/accept_timeout.cpp
==============================================================================
--- trunk/libs/asio/example/timeouts/accept_timeout.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
+++ (empty file)
@@ -1,74 +0,0 @@
-//
-// accept_timeout.cpp
-// ~~~~~~~~~~~~~~~~~~
-//
-// Copyright (c) 2003-2010 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 <boost/date_time/posix_time/posix_time_types.hpp>
-#include <iostream>
-
-using namespace boost::asio;
-using boost::asio::ip::tcp;
-
-class accept_handler
-{
-public:
- accept_handler(io_service& ios)
- : io_service_(ios),
- timer_(ios),
- acceptor_(ios, tcp::endpoint(tcp::v4(), 32123)),
- socket_(ios)
- {
- acceptor_.async_accept(socket_,
- boost::bind(&accept_handler::handle_accept, this,
- boost::asio::placeholders::error));
-
- timer_.expires_from_now(boost::posix_time::seconds(5));
- timer_.async_wait(boost::bind(&accept_handler::close, this));
- }
-
- void handle_accept(const boost::system::error_code& err)
- {
- if (err)
- {
- std::cout << "Accept error: " << err.message() << "\n";
- }
- else
- {
- std::cout << "Successful accept\n";
- }
- }
-
- void close()
- {
- acceptor_.close();
- }
-
-private:
- io_service& io_service_;
- deadline_timer timer_;
- tcp::acceptor acceptor_;
- tcp::socket socket_;
-};
-
-int main()
-{
- try
- {
- io_service ios;
- accept_handler ah(ios);
- ios.run();
- }
- catch (std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << "\n";
- }
-
- return 0;
-}
Added: trunk/libs/asio/example/timeouts/async_tcp_client.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/timeouts/async_tcp_client.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -0,0 +1,305 @@
+//
+// async_tcp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2010 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/bind.hpp>
+#include <iostream>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::tcp;
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Some asynchronous operations are given deadlines by which they must complete.
+// Deadlines are enforced by an "actor" that persists for the lifetime of the
+// client object:
+//
+// +----------------+
+// | |
+// | check_deadline |<---+
+// | | |
+// +----------------+ | async_wait()
+// | |
+// +---------+
+//
+// If the deadline actor determines that the deadline has expired, the socket
+// is closed and any outstanding operations are consequently cancelled.
+//
+// Connection establishment involves trying each endpoint in turn until a
+// connection is successful, or the available endpoints are exhausted. If the
+// deadline actor closes the socket, the connect actor is woken up and moves to
+// the next endpoint.
+//
+// +---------------+
+// | |
+// | start_connect |<---+
+// | | |
+// +---------------+ |
+// | |
+// async_- | +----------------+
+// connect() | | |
+// +--->| handle_connect |
+// | |
+// +----------------+
+// :
+// Once a connection is :
+// made, the connect :
+// actor forks in two - :
+// :
+// an actor for reading : and an actor for
+// inbound messages: : sending heartbeats:
+// :
+// +------------+ : +-------------+
+// | |<- - - - -+- - - - ->| |
+// | start_read | | start_write |<---+
+// | |<---+ | | |
+// +------------+ | +-------------+ | async_wait()
+// | | | |
+// async_- | +-------------+ async_- | +--------------+
+// read_- | | | write() | | |
+// until() +--->| handle_read | +--->| handle_write |
+// | | | |
+// +-------------+ +--------------+
+//
+// The input actor reads messages from the socket, where messages are delimited
+// by the newline character. The deadline for a complete message is 30 seconds.
+//
+// The heartbeat actor sends a heartbeat (a message that consists of a single
+// newline character) every 10 seconds. In this example, no deadline is applied
+// message sending.
+//
+class client
+{
+public:
+ client(boost::asio::io_service& io_service)
+ : stopped_(false),
+ socket_(io_service),
+ deadline_(io_service),
+ heartbeat_timer_(io_service)
+ {
+ }
+
+ // Called by the user of the client class to initiate the connection process.
+ // The endpoint iterator will have been obtained using a tcp::resolver.
+ void start(tcp::resolver::iterator endpoint_iter)
+ {
+ // Start the connect actor.
+ start_connect(endpoint_iter);
+
+ // Start the deadline actor. You will note that we're not setting any
+ // particular deadline here. Instead, the connect and input actors will
+ // update the deadline prior to each asynchronous operation.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+ // This function terminates all the actors to shut down the connection. It
+ // may be called by the user of the client class, or by the class itself in
+ // response to graceful termination or an unrecoverable error.
+ void stop()
+ {
+ stopped_ = true;
+ socket_.close();
+ deadline_.cancel();
+ heartbeat_timer_.cancel();
+ }
+
+private:
+ void start_connect(tcp::resolver::iterator endpoint_iter)
+ {
+ if (endpoint_iter != tcp::resolver::iterator())
+ {
+ std::cout << "Trying " << endpoint_iter->endpoint() << "...\n";
+
+ // Set a deadline for the connect operation.
+ deadline_.expires_from_now(boost::posix_time::seconds(60));
+
+ // Start the asynchronous connect operation.
+ socket_.async_connect(endpoint_iter->endpoint(),
+ boost::bind(&client::handle_connect,
+ this, _1, endpoint_iter));
+ }
+ else
+ {
+ // There are no more endpoints to try. Shut down the client.
+ stop();
+ }
+ }
+
+ void handle_connect(const boost::system::error_code& ec,
+ tcp::resolver::iterator endpoint_iter)
+ {
+ if (stopped_)
+ return;
+
+ // The async_connect() function automatically opens the socket at the start
+ // of the asynchronous operation. If the socket is closed at this time then
+ // the timeout handler must have run first.
+ if (!socket_.is_open())
+ {
+ std::cout << "Connect timed out\n";
+
+ // Try the next available endpoint.
+ start_connect(++endpoint_iter);
+ }
+
+ // Check if the connect operation failed before the deadline expired.
+ else if (ec)
+ {
+ std::cout << "Connect error: " << ec.message() << "\n";
+
+ // We need to close the socket used in the previous connection attempt
+ // before starting a new one.
+ socket_.close();
+
+ // Try the next available endpoint.
+ start_connect(++endpoint_iter);
+ }
+
+ // Otherwise we have successfully established a connection.
+ else
+ {
+ std::cout << "Connected to " << endpoint_iter->endpoint() << "\n";
+
+ // Start the input actor.
+ start_read();
+
+ // Start the heartbeat actor.
+ start_write();
+ }
+ }
+
+ void start_read()
+ {
+ // Set a deadline for the read operation.
+ deadline_.expires_from_now(boost::posix_time::seconds(30));
+
+ // Start an asynchronous operation to read a newline-delimited message.
+ boost::asio::async_read_until(socket_, input_buffer_, '\n',
+ boost::bind(&client::handle_read, this, _1));
+ }
+
+ void handle_read(const boost::system::error_code& ec)
+ {
+ if (stopped_)
+ return;
+
+ if (!ec)
+ {
+ // Extract the newline-delimited message from the buffer.
+ std::string line;
+ std::istream is(&input_buffer_);
+ std::getline(is, line);
+
+ // Empty messages are heartbeats and so ignored.
+ if (!line.empty())
+ {
+ std::cout << "Received: " << line << "\n";
+ }
+
+ start_read();
+ }
+ else
+ {
+ std::cout << "Error on receive: " << ec.message() << "\n";
+
+ stop();
+ }
+ }
+
+ void start_write()
+ {
+ if (stopped_)
+ return;
+
+ // Start an asynchronous operation to send a heartbeat message.
+ boost::asio::async_write(socket_, boost::asio::buffer("\n", 1),
+ boost::bind(&client::handle_write, this, _1));
+ }
+
+ void handle_write(const boost::system::error_code& ec)
+ {
+ if (stopped_)
+ return;
+
+ if (!ec)
+ {
+ // Wait 10 seconds before sending the next heartbeat.
+ heartbeat_timer_.expires_from_now(boost::posix_time::seconds(10));
+ heartbeat_timer_.async_wait(boost::bind(&client::start_write, this));
+ }
+ else
+ {
+ std::cout << "Error on heartbeat: " << ec.message() << "\n";
+
+ stop();
+ }
+ }
+
+ void check_deadline()
+ {
+ if (stopped_)
+ return;
+
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (deadline_.expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled.
+ socket_.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+private:
+ bool stopped_;
+ tcp::socket socket_;
+ boost::asio::streambuf input_buffer_;
+ deadline_timer deadline_;
+ deadline_timer heartbeat_timer_;
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 3)
+ {
+ std::cerr << "Usage: client <host> <port>\n";
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+ tcp::resolver r(io_service);
+ client c(io_service);
+
+ c.start(r.resolve(tcp::resolver::query(argv[1], argv[2])));
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
Added: trunk/libs/asio/example/timeouts/blocking_tcp_client.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/timeouts/blocking_tcp_client.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -0,0 +1,250 @@
+//
+// blocking_tcp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2010 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/system/system_error.hpp>
+#include <boost/asio/write.hpp>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <boost/lambda/bind.hpp>
+#include <boost/lambda/lambda.hpp>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::tcp;
+using boost::lambda::bind;
+using boost::lambda::var;
+using boost::lambda::_1;
+
+//----------------------------------------------------------------------
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Each asynchronous operation is given a deadline by which it must complete.
+// Deadlines are enforced by an "actor" that persists for the lifetime of the
+// client object:
+//
+// +----------------+
+// | |
+// | check_deadline |<---+
+// | | |
+// +----------------+ | async_wait()
+// | |
+// +---------+
+//
+// If the actor determines that the deadline has expired, the socket is closed
+// and any outstanding operations are consequently cancelled. The socket
+// operations themselves use boost::lambda function objects as completion
+// handlers. For a given socket operation, the client object runs the
+// io_service to block thread execution until the actor completes.
+//
+class client
+{
+public:
+ client()
+ : socket_(io_service_),
+ deadline_(io_service_)
+ {
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+ }
+
+ void connect(const std::string& host, const std::string& service,
+ boost::posix_time::time_duration timeout)
+ {
+ // Resolve the host name and service to a list of endpoints.
+ tcp::resolver::query query(host, service);
+ tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
+
+ // Set a deadline for the asynchronous operation. The host name may resolve
+ // to multiple endpoints, and this function tries to connect to each one in
+ // turn. Setting the deadline here means it applies to the entire sequence.
+ deadline_.expires_from_now(timeout);
+
+ boost::system::error_code ec;
+
+ for (; iter != tcp::resolver::iterator(); ++iter)
+ {
+ // We may have an open socket from a previous connection attempt. This
+ // socket cannot be reused, so we must close it before trying to connect
+ // again.
+ socket_.close();
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ socket_.async_connect(iter->endpoint(), var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ // Determine whether a connection was successfully established. The
+ // deadline actor may have had a chance to run and close our socket, even
+ // though the connect operation notionally succeeded. Therefore we must
+ // check whether the socket is still open before deciding that the we
+ // were successful.
+ if (!ec && socket_.is_open())
+ return;
+ }
+
+ throw boost::system::system_error(
+ ec ? ec : boost::asio::error::host_not_found);
+ }
+
+ std::string read_line(boost::posix_time::time_duration timeout)
+ {
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_read_until), the deadline applies to the
+ // entire operation, rather than individual reads from the socket.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_read_until(socket_, input_buffer_, '\n', var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ if (ec)
+ throw boost::system::system_error(ec);
+
+ std::string line;
+ std::istream is(&input_buffer_);
+ std::getline(is, line);
+ return line;
+ }
+
+ void write_line(const std::string& line,
+ boost::posix_time::time_duration timeout)
+ {
+ std::string data = line + "\n";
+
+ // Set a deadline for the asynchronous operation. Since this function uses
+ // a composed operation (async_write), the deadline applies to the entire
+ // operation, rather than individual writes to the socket.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variable that receives the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ boost::system::error_code ec = boost::asio::error::would_block;
+
+ // Start the asynchronous operation itself. The boost::lambda function
+ // object is used as a callback and will update the ec variable when the
+ // operation completes. The blocking_udp_client.cpp example shows how you
+ // can use boost::bind rather than boost::lambda.
+ boost::asio::async_write(socket_, boost::asio::buffer(data), var(ec) = _1);
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ if (ec)
+ throw boost::system::system_error(ec);
+ }
+
+private:
+ void check_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (deadline_.expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The socket is closed so that any outstanding
+ // asynchronous operations are cancelled. This allows the blocked
+ // connect(), read_line() or write_line() functions to return.
+ socket_.close();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(bind(&client::check_deadline, this));
+ }
+
+ boost::asio::io_service io_service_;
+ tcp::socket socket_;
+ deadline_timer deadline_;
+ boost::asio::streambuf input_buffer_;
+};
+
+//----------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 4)
+ {
+ std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
+ return 1;
+ }
+
+ client c;
+ c.connect(argv[1], argv[2], boost::posix_time::seconds(10));
+
+ boost::posix_time::ptime time_sent =
+ boost::posix_time::microsec_clock::universal_time();
+
+ c.write_line(argv[3], boost::posix_time::seconds(10));
+
+ for (;;)
+ {
+ std::string line = c.read_line(boost::posix_time::seconds(10));
+
+ // Keep going until we get back the line that was sent.
+ if (line == argv[3])
+ break;
+ }
+
+ boost::posix_time::ptime time_received =
+ boost::posix_time::microsec_clock::universal_time();
+
+ std::cout << "Round trip time: ";
+ std::cout << (time_received - time_sent).total_microseconds();
+ std::cout << " microseconds\n";
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
Added: trunk/libs/asio/example/timeouts/blocking_udp_client.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/timeouts/blocking_udp_client.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -0,0 +1,182 @@
+//
+// blocking_udp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2010 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/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <cstdlib>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+#include <iostream>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::udp;
+
+//----------------------------------------------------------------------
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Each asynchronous operation is given a deadline by which it must complete.
+// Deadlines are enforced by an "actor" that persists for the lifetime of the
+// client object:
+//
+// +----------------+
+// | |
+// | check_deadline |<---+
+// | | |
+// +----------------+ | async_wait()
+// | |
+// +---------+
+//
+// If the actor determines that the deadline has expired, any outstanding
+// socket operations are cancelled. The socket operations themselves are
+// implemented as transient actors:
+//
+// +---------------+
+// | |
+// | receive |
+// | |
+// +---------------+
+// |
+// async_- | +----------------+
+// receive() | | |
+// +--->| handle_receive |
+// | |
+// +----------------+
+//
+// The client object runs the io_service to block thread execution until the
+// actor completes.
+//
+class client
+{
+public:
+ client(const udp::endpoint& listen_endpoint)
+ : socket_(io_service_, listen_endpoint),
+ deadline_(io_service_)
+ {
+ // No deadline is required until the first socket operation is started. We
+ // set the deadline to positive infinity so that the actor takes no action
+ // until a specific deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // Start the persistent actor that checks for deadline expiry.
+ check_deadline();
+ }
+
+ std::size_t receive(const boost::asio::mutable_buffer& buffer,
+ boost::posix_time::time_duration timeout, boost::system::error_code& ec)
+ {
+ // Set a deadline for the asynchronous operation.
+ deadline_.expires_from_now(timeout);
+
+ // Set up the variables that receive the result of the asynchronous
+ // operation. The error code is set to would_block to signal that the
+ // operation is incomplete. Asio guarantees that its asynchronous
+ // operations will never fail with would_block, so any other value in
+ // ec indicates completion.
+ ec = boost::asio::error::would_block;
+ std::size_t length = 0;
+
+ // Start the asynchronous operation itself. The handle_receive function
+ // used as a callback will update the ec and length variables.
+ socket_.async_receive(boost::asio::buffer(buffer),
+ boost::bind(&client::handle_receive, _1, _2, &ec, &length));
+
+ // Block until the asynchronous operation has completed.
+ do io_service_.run_one(); while (ec == boost::asio::error::would_block);
+
+ return length;
+ }
+
+private:
+ void check_deadline()
+ {
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (deadline_.expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. The outstanding asynchronous operation needs
+ // to be cancelled so that the blocked receive() function will return.
+ //
+ // Please note that cancel() has portability issues on some versions of
+ // Microsoft Windows, and it may be necessary to use close() instead.
+ // Consult the documentation for cancel() for further information.
+ socket_.cancel();
+
+ // There is no longer an active deadline. The expiry is set to positive
+ // infinity so that the actor takes no action until a new deadline is set.
+ deadline_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+ static void handle_receive(
+ const boost::system::error_code& ec, std::size_t length,
+ boost::system::error_code* out_ec, std::size_t* out_length)
+ {
+ *out_ec = ec;
+ *out_length = length;
+ }
+
+private:
+ boost::asio::io_service io_service_;
+ udp::socket socket_;
+ deadline_timer deadline_;
+};
+
+//----------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ using namespace std; // For atoi.
+
+ if (argc != 3)
+ {
+ std::cerr << "Usage: blocking_udp_timeout <listen_addr> <listen_port>\n";
+ return 1;
+ }
+
+ udp::endpoint listen_endpoint(
+ boost::asio::ip::address::from_string(argv[1]),
+ std::atoi(argv[2]));
+
+ client c(listen_endpoint);
+
+ for (;;)
+ {
+ char data[1024];
+ boost::system::error_code ec;
+ std::size_t n = c.receive(boost::asio::buffer(data),
+ boost::posix_time::seconds(10), ec);
+
+ if (ec)
+ {
+ std::cout << "Receive error: " << ec.message() << "\n";
+ }
+ else
+ {
+ std::cout << "Received: ";
+ std::cout.write(data, n);
+ std::cout << "\n";
+ }
+ }
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
Deleted: trunk/libs/asio/example/timeouts/connect_timeout.cpp
==============================================================================
--- trunk/libs/asio/example/timeouts/connect_timeout.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
+++ (empty file)
@@ -1,85 +0,0 @@
-//
-// connect_timeout.cpp
-// ~~~~~~~~~~~~~~~~~~~
-//
-// Copyright (c) 2003-2010 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 <boost/date_time/posix_time/posix_time_types.hpp>
-#include <iostream>
-
-using namespace boost::asio;
-using boost::asio::ip::tcp;
-
-class connect_handler
-{
-public:
- connect_handler(io_service& ios)
- : io_service_(ios),
- timer_(ios),
- socket_(ios)
- {
- socket_.async_connect(
- tcp::endpoint(boost::asio::ip::address_v4::loopback(), 32123),
- boost::bind(&connect_handler::handle_connect, this,
- boost::asio::placeholders::error));
-
- timer_.expires_from_now(boost::posix_time::seconds(5));
- timer_.async_wait(boost::bind(&connect_handler::close, this));
- }
-
- void handle_connect(const boost::system::error_code& err)
- {
- if (err)
- {
- std::cout << "Connect error: " << err.message() << "\n";
- }
- else
- {
- std::cout << "Successful connection\n";
- }
- }
-
- void close()
- {
- socket_.close();
- }
-
-private:
- io_service& io_service_;
- deadline_timer timer_;
- tcp::socket socket_;
-};
-
-int main()
-{
- try
- {
- io_service ios;
- tcp::acceptor a(ios, tcp::endpoint(tcp::v4(), 32123), 1);
-
- // Make lots of connections so that at least some of them will block.
- connect_handler ch1(ios);
- connect_handler ch2(ios);
- connect_handler ch3(ios);
- connect_handler ch4(ios);
- connect_handler ch5(ios);
- connect_handler ch6(ios);
- connect_handler ch7(ios);
- connect_handler ch8(ios);
- connect_handler ch9(ios);
-
- ios.run();
- }
- catch (std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << "\n";
- }
-
- return 0;
-}
Deleted: trunk/libs/asio/example/timeouts/datagram_receive_timeout.cpp
==============================================================================
--- trunk/libs/asio/example/timeouts/datagram_receive_timeout.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
+++ (empty file)
@@ -1,78 +0,0 @@
-//
-// datagram_receive_timeout.cpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-// Copyright (c) 2003-2010 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 <boost/date_time/posix_time/posix_time_types.hpp>
-#include <iostream>
-
-using namespace boost::asio;
-using boost::asio::ip::udp;
-
-class datagram_handler
-{
-public:
- datagram_handler(io_service& ios)
- : io_service_(ios),
- timer_(ios),
- socket_(ios, udp::endpoint(udp::v4(), 32124))
- {
- socket_.async_receive_from(
- boost::asio::buffer(data_, max_length), sender_endpoint_,
- boost::bind(&datagram_handler::handle_receive_from, this,
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred));
-
- timer_.expires_from_now(boost::posix_time::seconds(5));
- timer_.async_wait(boost::bind(&datagram_handler::close, this));
- }
-
- void handle_receive_from(const boost::system::error_code& err,
- size_t /*length*/)
- {
- if (err)
- {
- std::cout << "Receive error: " << err.message() << "\n";
- }
- else
- {
- std::cout << "Successful receive\n";
- }
- }
-
- void close()
- {
- socket_.close();
- }
-
-private:
- io_service& io_service_;
- deadline_timer timer_;
- udp::socket socket_;
- udp::endpoint sender_endpoint_;
- enum { max_length = 512 };
- char data_[max_length];
-};
-
-int main()
-{
- try
- {
- io_service ios;
- datagram_handler dh(ios);
- ios.run();
- }
- catch (std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << "\n";
- }
-
- return 0;
-}
Added: trunk/libs/asio/example/timeouts/server.cpp
==============================================================================
--- (empty file)
+++ trunk/libs/asio/example/timeouts/server.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
@@ -0,0 +1,424 @@
+//
+// server.cpp
+// ~~~~~~~~~~
+//
+// Copyright (c) 2003-2010 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 <algorithm>
+#include <cstdlib>
+#include <deque>
+#include <iostream>
+#include <set>
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/io_service.hpp>
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/read_until.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/write.hpp>
+
+using boost::asio::deadline_timer;
+using boost::asio::ip::tcp;
+using boost::asio::ip::udp;
+
+//----------------------------------------------------------------------
+
+class subscriber
+{
+public:
+ virtual ~subscriber() {}
+ virtual void deliver(const std::string& msg) = 0;
+};
+
+typedef boost::shared_ptr<subscriber> subscriber_ptr;
+
+//----------------------------------------------------------------------
+
+class channel
+{
+public:
+ void join(subscriber_ptr subscriber)
+ {
+ subscribers_.insert(subscriber);
+ }
+
+ void leave(subscriber_ptr subscriber)
+ {
+ subscribers_.erase(subscriber);
+ }
+
+ void deliver(const std::string& msg)
+ {
+ std::for_each(subscribers_.begin(), subscribers_.end(),
+ boost::bind(&subscriber::deliver, _1, boost::ref(msg)));
+ }
+
+private:
+ std::set<subscriber_ptr> subscribers_;
+};
+
+//----------------------------------------------------------------------
+
+//
+// This class manages socket timeouts by applying the concept of a deadline.
+// Some asynchronous operations are given deadlines by which they must complete.
+// Deadlines are enforced by two "actors" that persist for the lifetime of the
+// session object, one for input and one for output:
+//
+// +----------------+ +----------------+
+// | | | |
+// | check_deadline |<---+ | check_deadline |<---+
+// | | | async_wait() | | | async_wait()
+// +----------------+ | on input +----------------+ | on output
+// | | deadline | | deadline
+// +---------+ +---------+
+//
+// If either deadline actor determines that the corresponding deadline has
+// expired, the socket is closed and any outstanding operations are cancelled.
+//
+// The input actor reads messages from the socket, where messages are delimited
+// by the newline character:
+//
+// +------------+
+// | |
+// | start_read |<---+
+// | | |
+// +------------+ |
+// | |
+// async_- | +-------------+
+// read_- | | |
+// until() +--->| handle_read |
+// | |
+// +-------------+
+//
+// The deadline for receiving a complete message is 30 seconds. If a non-empty
+// message is received, it is delivered to all subscribers. If a heartbeat (a
+// message that consists of a single newline character) is received, a heartbeat
+// is enqueued for the client, provided there are no other messages waiting to
+// be sent.
+//
+// The output actor is responsible for sending messages to the client:
+//
+// +--------------+
+// | |<---------------------+
+// | await_output | |
+// | |<---+ |
+// +--------------+ | |
+// | | | async_wait() |
+// | +--------+ |
+// V |
+// +-------------+ +--------------+
+// | | async_write() | |
+// | start_write |-------------->| handle_write |
+// | | | |
+// +-------------+ +--------------+
+//
+// The output actor first waits for an output message to be enqueued. It does
+// this by using a deadline_timer as an asynchronous condition variable. The
+// deadline_timer will be signalled whenever the output queue is non-empty.
+//
+// Once a message is available, it is sent to the client. The deadline for
+// sending a complete message is 30 seconds. After the message is successfully
+// sent, the output actor again waits for the output queue to become non-empty.
+//
+class tcp_session
+ : public subscriber,
+ public boost::enable_shared_from_this<tcp_session>
+{
+public:
+ tcp_session(boost::asio::io_service& io_service, channel& ch)
+ : channel_(ch),
+ socket_(io_service),
+ input_deadline_(io_service),
+ non_empty_output_queue_(io_service),
+ output_deadline_(io_service)
+ {
+ input_deadline_.expires_at(boost::posix_time::pos_infin);
+ output_deadline_.expires_at(boost::posix_time::pos_infin);
+
+ // The non_empty_output_queue_ deadline_timer is set to pos_infin whenever
+ // the output queue is empty. This ensures that the output actor stays
+ // asleep until a message is put into the queue.
+ non_empty_output_queue_.expires_at(boost::posix_time::pos_infin);
+ }
+
+ tcp::socket& socket()
+ {
+ return socket_;
+ }
+
+ // Called by the server object to initiate the four actors.
+ void start()
+ {
+ channel_.join(shared_from_this());
+
+ start_read();
+
+ input_deadline_.async_wait(
+ boost::bind(&tcp_session::check_deadline,
+ shared_from_this(), &input_deadline_));
+
+ await_output();
+
+ output_deadline_.async_wait(
+ boost::bind(&tcp_session::check_deadline,
+ shared_from_this(), &output_deadline_));
+ }
+
+private:
+ void stop()
+ {
+ channel_.leave(shared_from_this());
+
+ socket_.close();
+ input_deadline_.cancel();
+ non_empty_output_queue_.cancel();
+ output_deadline_.cancel();
+ }
+
+ bool stopped() const
+ {
+ return !socket_.is_open();
+ }
+
+ void deliver(const std::string& msg)
+ {
+ output_queue_.push_back(msg + "\n");
+
+ // Signal that the output queue contains messages. Modifying the expiry
+ // will wake the output actor, if it is waiting on the timer.
+ non_empty_output_queue_.expires_at(boost::posix_time::neg_infin);
+ }
+
+ void start_read()
+ {
+ // Set a deadline for the read operation.
+ input_deadline_.expires_from_now(boost::posix_time::seconds(30));
+
+ // Start an asynchronous operation to read a newline-delimited message.
+ boost::asio::async_read_until(socket_, input_buffer_, '\n',
+ boost::bind(&tcp_session::handle_read, shared_from_this(), _1));
+ }
+
+ void handle_read(const boost::system::error_code& ec)
+ {
+ if (stopped())
+ return;
+
+ if (!ec)
+ {
+ // Extract the newline-delimited message from the buffer.
+ std::string msg;
+ std::istream is(&input_buffer_);
+ std::getline(is, msg);
+
+ if (!msg.empty())
+ {
+ channel_.deliver(msg);
+ }
+ else
+ {
+ // We received a heartbeat message from the client. If there's nothing
+ // else being sent or ready to be sent, send a heartbeat right back.
+ if (output_queue_.empty())
+ {
+ output_queue_.push_back("\n");
+
+ // Signal that the output queue contains messages. Modifying the
+ // expiry will wake the output actor, if it is waiting on the timer.
+ non_empty_output_queue_.expires_at(boost::posix_time::neg_infin);
+ }
+ }
+
+ start_read();
+ }
+ else
+ {
+ stop();
+ }
+ }
+
+ void await_output()
+ {
+ if (stopped())
+ return;
+
+ if (output_queue_.empty())
+ {
+ // There are no messages that are ready to be sent. The actor goes to
+ // sleep by waiting on the non_empty_output_queue_ timer. When a new
+ // message is added, the timer will be modified and the actor will wake.
+ non_empty_output_queue_.expires_at(boost::posix_time::pos_infin);
+ non_empty_output_queue_.async_wait(
+ boost::bind(&tcp_session::await_output, shared_from_this()));
+ }
+ else
+ {
+ start_write();
+ }
+ }
+
+ void start_write()
+ {
+ // Set a deadline for the write operation.
+ output_deadline_.expires_from_now(boost::posix_time::seconds(30));
+
+ // Start an asynchronous operation to send a message.
+ boost::asio::async_write(socket_,
+ boost::asio::buffer(output_queue_.front()),
+ boost::bind(&tcp_session::handle_write, shared_from_this(), _1));
+ }
+
+ void handle_write(const boost::system::error_code& ec)
+ {
+ if (stopped())
+ return;
+
+ if (!ec)
+ {
+ output_queue_.pop_front();
+
+ await_output();
+ }
+ else
+ {
+ stop();
+ }
+ }
+
+ void check_deadline(deadline_timer* deadline)
+ {
+ if (stopped())
+ return;
+
+ // Check whether the deadline has passed. We compare the deadline against
+ // the current time since a new asynchronous operation may have moved the
+ // deadline before this actor had a chance to run.
+ if (deadline->expires_at() <= deadline_timer::traits_type::now())
+ {
+ // The deadline has passed. Stop the session. The other actors will
+ // terminate as soon as possible.
+ stop();
+ }
+ else
+ {
+ // Put the actor back to sleep.
+ deadline->async_wait(
+ boost::bind(&tcp_session::check_deadline,
+ shared_from_this(), deadline));
+ }
+ }
+
+ channel& channel_;
+ tcp::socket socket_;
+ boost::asio::streambuf input_buffer_;
+ deadline_timer input_deadline_;
+ std::deque<std::string> output_queue_;
+ deadline_timer non_empty_output_queue_;
+ deadline_timer output_deadline_;
+};
+
+typedef boost::shared_ptr<tcp_session> tcp_session_ptr;
+
+//----------------------------------------------------------------------
+
+class udp_broadcaster
+ : public subscriber
+{
+public:
+ udp_broadcaster(boost::asio::io_service& io_service,
+ const udp::endpoint& broadcast_endpoint)
+ : socket_(io_service)
+ {
+ socket_.connect(broadcast_endpoint);
+ }
+
+private:
+ void deliver(const std::string& msg)
+ {
+ boost::system::error_code ignored_ec;
+ socket_.send(boost::asio::buffer(msg), 0, ignored_ec);
+ }
+
+ udp::socket socket_;
+};
+
+//----------------------------------------------------------------------
+
+class server
+{
+public:
+ server(boost::asio::io_service& io_service,
+ const tcp::endpoint& listen_endpoint,
+ const udp::endpoint& broadcast_endpoint)
+ : io_service_(io_service),
+ acceptor_(io_service, listen_endpoint)
+ {
+ subscriber_ptr bc(new udp_broadcaster(io_service_, broadcast_endpoint));
+ channel_.join(bc);
+
+ tcp_session_ptr new_session(new tcp_session(io_service_, channel_));
+
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session, _1));
+ }
+
+ void handle_accept(tcp_session_ptr session,
+ const boost::system::error_code& ec)
+ {
+ if (!ec)
+ {
+ session->start();
+
+ tcp_session_ptr new_session(new tcp_session(io_service_, channel_));
+
+ acceptor_.async_accept(new_session->socket(),
+ boost::bind(&server::handle_accept, this, new_session, _1));
+ }
+ }
+
+private:
+ boost::asio::io_service& io_service_;
+ tcp::acceptor acceptor_;
+ channel channel_;
+};
+
+//----------------------------------------------------------------------
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ using namespace std; // For atoi.
+
+ if (argc != 4)
+ {
+ std::cerr << "Usage: server <listen_port> <bcast_address> <bcast_port>\n";
+ return 1;
+ }
+
+ boost::asio::io_service io_service;
+
+ tcp::endpoint listen_endpoint(tcp::v4(), atoi(argv[1]));
+
+ udp::endpoint broadcast_endpoint(
+ boost::asio::ip::address::from_string(argv[2]), atoi(argv[3]));
+
+ server s(io_service, listen_endpoint, broadcast_endpoint);
+
+ io_service.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}
Deleted: trunk/libs/asio/example/timeouts/stream_receive_timeout.cpp
==============================================================================
--- trunk/libs/asio/example/timeouts/stream_receive_timeout.cpp 2010-07-04 02:53:57 EDT (Sun, 04 Jul 2010)
+++ (empty file)
@@ -1,102 +0,0 @@
-//
-// stream_receive_timeout.cpp
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-// Copyright (c) 2003-2010 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 <boost/date_time/posix_time/posix_time_types.hpp>
-#include <iostream>
-
-using namespace boost::asio;
-using boost::asio::ip::tcp;
-
-class stream_handler
-{
-public:
- stream_handler(io_service& ios)
- : io_service_(ios),
- timer_(ios),
- acceptor_(ios, tcp::endpoint(tcp::v4(), 32123)),
- socket_(ios)
- {
- acceptor_.async_accept(socket_,
- boost::bind(&stream_handler::handle_accept, this,
- boost::asio::placeholders::error));
- }
-
- void handle_accept(const boost::system::error_code& err)
- {
- if (err)
- {
- std::cout << "Accept error: " << err.message() << "\n";
- }
- else
- {
- std::cout << "Successful accept\n";
-
- socket_.async_read_some(boost::asio::buffer(buf_, sizeof(buf_)),
- boost::bind(&stream_handler::handle_recv, this,
- boost::asio::placeholders::error));
- timer_.expires_from_now(boost::posix_time::seconds(5));
- timer_.async_wait(boost::bind(&stream_handler::close, this));
- }
- }
-
- void handle_recv(const boost::system::error_code& err)
- {
- if (err)
- {
- std::cout << "Receive error: " << err.message() << "\n";
- }
- else
- {
- std::cout << "Successful receive\n";
- }
- }
-
- void close()
- {
- socket_.close();
- }
-
-private:
- io_service& io_service_;
- deadline_timer timer_;
- tcp::acceptor acceptor_;
- tcp::socket socket_;
- char buf_[1024];
-};
-
-void connect_handler()
-{
- std::cout << "Successful connect\n";
-}
-
-int main()
-{
- try
- {
- io_service ios;
-
- stream_handler sh(ios);
-
- tcp::socket s(ios);
- s.async_connect(
- tcp::endpoint(boost::asio::ip::address_v4::loopback(), 32123),
- boost::bind(connect_handler));
-
- ios.run();
- }
- catch (std::exception& e)
- {
- std::cerr << "Exception: " << e.what() << "\n";
- }
-
- return 0;
-}
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