summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp')
-rw-r--r--3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp311
1 files changed, 311 insertions, 0 deletions
diff --git a/3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp b/3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp
new file mode 100644
index 00000000000..fe2d27fac9e
--- /dev/null
+++ b/3rdparty/asio/src/examples/cpp03/timeouts/async_tcp_client.cpp
@@ -0,0 +1,311 @@
+//
+// async_tcp_client.cpp
+// ~~~~~~~~~~~~~~~~~~~~
+//
+// Copyright (c) 2003-2021 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/buffer.hpp"
+#include "asio/io_context.hpp"
+#include "asio/ip/tcp.hpp"
+#include "asio/read_until.hpp"
+#include "asio/steady_timer.hpp"
+#include "asio/write.hpp"
+#include <boost/bind/bind.hpp>
+#include <iostream>
+#include <string>
+
+using asio::steady_timer;
+using 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
+// to message sending.
+//
+class client
+{
+public:
+ client(asio::io_context& io_context)
+ : stopped_(false),
+ socket_(io_context),
+ deadline_(io_context),
+ heartbeat_timer_(io_context)
+ {
+ }
+
+ // Called by the user of the client class to initiate the connection process.
+ // The endpoints will have been obtained using a tcp::resolver.
+ void start(tcp::resolver::results_type endpoints)
+ {
+ // Start the connect actor.
+ endpoints_ = endpoints;
+ start_connect(endpoints_.begin());
+
+ // 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;
+ asio::error_code ignored_ec;
+ socket_.close(ignored_ec);
+ deadline_.cancel();
+ heartbeat_timer_.cancel();
+ }
+
+private:
+ void start_connect(tcp::resolver::results_type::iterator endpoint_iter)
+ {
+ if (endpoint_iter != endpoints_.end())
+ {
+ std::cout << "Trying " << endpoint_iter->endpoint() << "...\n";
+
+ // Set a deadline for the connect operation.
+ deadline_.expires_after(asio::chrono::seconds(60));
+
+ // Start the asynchronous connect operation.
+ socket_.async_connect(endpoint_iter->endpoint(),
+ boost::bind(&client::handle_connect, this,
+ boost::placeholders::_1, endpoint_iter));
+ }
+ else
+ {
+ // There are no more endpoints to try. Shut down the client.
+ stop();
+ }
+ }
+
+ void handle_connect(const asio::error_code& ec,
+ tcp::resolver::results_type::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_after(asio::chrono::seconds(30));
+
+ // Start an asynchronous operation to read a newline-delimited message.
+ asio::async_read_until(socket_,
+ asio::dynamic_buffer(input_buffer_), '\n',
+ boost::bind(&client::handle_read, this,
+ boost::placeholders::_1, boost::placeholders::_2));
+ }
+
+ void handle_read(const asio::error_code& ec, std::size_t n)
+ {
+ if (stopped_)
+ return;
+
+ if (!ec)
+ {
+ // Extract the newline-delimited message from the buffer.
+ std::string line(input_buffer_.substr(0, n - 1));
+ input_buffer_.erase(0, n);
+
+ // 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.
+ asio::async_write(socket_, asio::buffer("\n", 1),
+ boost::bind(&client::handle_write, this, boost::placeholders::_1));
+ }
+
+ void handle_write(const asio::error_code& ec)
+ {
+ if (stopped_)
+ return;
+
+ if (!ec)
+ {
+ // Wait 10 seconds before sending the next heartbeat.
+ heartbeat_timer_.expires_after(asio::chrono::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_.expiry() <= steady_timer::clock_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 the
+ // maximum time point so that the actor takes no action until a new
+ // deadline is set.
+ deadline_.expires_at(steady_timer::time_point::max());
+ }
+
+ // Put the actor back to sleep.
+ deadline_.async_wait(boost::bind(&client::check_deadline, this));
+ }
+
+private:
+ bool stopped_;
+ tcp::resolver::results_type endpoints_;
+ tcp::socket socket_;
+ std::string input_buffer_;
+ steady_timer deadline_;
+ steady_timer heartbeat_timer_;
+};
+
+int main(int argc, char* argv[])
+{
+ try
+ {
+ if (argc != 3)
+ {
+ std::cerr << "Usage: client <host> <port>\n";
+ return 1;
+ }
+
+ asio::io_context io_context;
+ tcp::resolver r(io_context);
+ client c(io_context);
+
+ c.start(r.resolve(argv[1], argv[2]));
+
+ io_context.run();
+ }
+ catch (std::exception& e)
+ {
+ std::cerr << "Exception: " << e.what() << "\n";
+ }
+
+ return 0;
+}