diff options
-rw-r--r-- | 3rdparty/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/3rdparty/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp b/3rdparty/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp new file mode 100644 index 00000000000..69d467af1bb --- /dev/null +++ b/3rdparty/asio/src/examples/cpp03/timeouts/blocking_tcp_client.cpp @@ -0,0 +1,242 @@ +// +// blocking_tcp_client.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2016 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 "asio/connect.hpp" +#include "asio/deadline_timer.hpp" +#include "asio/io_context.hpp" +#include "asio/ip/tcp.hpp" +#include "asio/read_until.hpp" +#include "asio/streambuf.hpp" +#include "asio/system_error.hpp" +#include "asio/write.hpp" +#include "asio/thread.hpp" +#include <cstdlib> +#include <iostream> +#include <string> +#include <boost/lambda/bind.hpp> +#include <boost/lambda/lambda.hpp> + +using asio::deadline_timer; +using 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_context to block thread execution until the actor completes. +// +class client +{ +public: + client() + : socket_(io_context_), + deadline_(io_context_) + { + // 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::results_type endpoints = + tcp::resolver(io_context_).resolve(host, service); + + // Set a deadline for the asynchronous operation. As a host name may + // resolve to multiple endpoints, this function uses the composed operation + // async_connect. The deadline applies to the entire operation, rather than + // individual connection attempts. + 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. + asio::error_code ec = 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. + asio::async_connect(socket_, endpoints, var(ec) = _1); + + // Block until the asynchronous operation has completed. + do io_context_.run_one(); while (ec == 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 if we succeeded + // or failed. + if (ec || !socket_.is_open()) + throw asio::system_error( + ec ? ec : asio::error::operation_aborted); + } + + 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. + asio::error_code ec = 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. + asio::async_read_until(socket_, input_buffer_, '\n', var(ec) = _1); + + // Block until the asynchronous operation has completed. + do io_context_.run_one(); while (ec == asio::error::would_block); + + if (ec) + throw asio::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. + asio::error_code ec = 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. + asio::async_write(socket_, asio::buffer(data), var(ec) = _1); + + // Block until the asynchronous operation has completed. + do io_context_.run_one(); while (ec == asio::error::would_block); + + if (ec) + throw asio::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. + asio::error_code ignored_ec; + socket_.close(ignored_ec); + + // 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)); + } + + asio::io_context io_context_; + tcp::socket socket_; + deadline_timer deadline_; + 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; +} |