]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/asio/example/cpp03/timeouts/blocking_tcp_client.cpp
update sources to v12.2.3
[ceph.git] / ceph / src / boost / libs / asio / example / cpp03 / timeouts / blocking_tcp_client.cpp
CommitLineData
7c673cae
FG
1//
2// blocking_tcp_client.cpp
3// ~~~~~~~~~~~~~~~~~~~~~~~
4//
b32b8144 5// Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot com)
7c673cae
FG
6//
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)
9//
10
11#include <boost/asio/connect.hpp>
12#include <boost/asio/deadline_timer.hpp>
b32b8144 13#include <boost/asio/io_context.hpp>
7c673cae
FG
14#include <boost/asio/ip/tcp.hpp>
15#include <boost/asio/read_until.hpp>
16#include <boost/asio/streambuf.hpp>
17#include <boost/system/system_error.hpp>
18#include <boost/asio/write.hpp>
19#include <cstdlib>
20#include <iostream>
21#include <string>
22#include <boost/lambda/bind.hpp>
23#include <boost/lambda/lambda.hpp>
24
25using boost::asio::deadline_timer;
26using boost::asio::ip::tcp;
27using boost::lambda::bind;
28using boost::lambda::var;
29using boost::lambda::_1;
30
31//----------------------------------------------------------------------
32
33//
34// This class manages socket timeouts by applying the concept of a deadline.
35// Each asynchronous operation is given a deadline by which it must complete.
36// Deadlines are enforced by an "actor" that persists for the lifetime of the
37// client object:
38//
39// +----------------+
40// | |
41// | check_deadline |<---+
42// | | |
43// +----------------+ | async_wait()
44// | |
45// +---------+
46//
47// If the actor determines that the deadline has expired, the socket is closed
48// and any outstanding operations are consequently cancelled. The socket
49// operations themselves use boost::lambda function objects as completion
50// handlers. For a given socket operation, the client object runs the
b32b8144 51// io_context to block thread execution until the actor completes.
7c673cae
FG
52//
53class client
54{
55public:
56 client()
b32b8144
FG
57 : socket_(io_context_),
58 deadline_(io_context_)
7c673cae
FG
59 {
60 // No deadline is required until the first socket operation is started. We
61 // set the deadline to positive infinity so that the actor takes no action
62 // until a specific deadline is set.
63 deadline_.expires_at(boost::posix_time::pos_infin);
64
65 // Start the persistent actor that checks for deadline expiry.
66 check_deadline();
67 }
68
69 void connect(const std::string& host, const std::string& service,
70 boost::posix_time::time_duration timeout)
71 {
72 // Resolve the host name and service to a list of endpoints.
b32b8144
FG
73 tcp::resolver::results_type endpoints =
74 tcp::resolver(io_context_).resolve(host, service);
7c673cae
FG
75
76 // Set a deadline for the asynchronous operation. As a host name may
77 // resolve to multiple endpoints, this function uses the composed operation
78 // async_connect. The deadline applies to the entire operation, rather than
79 // individual connection attempts.
80 deadline_.expires_from_now(timeout);
81
82 // Set up the variable that receives the result of the asynchronous
83 // operation. The error code is set to would_block to signal that the
84 // operation is incomplete. Asio guarantees that its asynchronous
85 // operations will never fail with would_block, so any other value in
86 // ec indicates completion.
87 boost::system::error_code ec = boost::asio::error::would_block;
88
89 // Start the asynchronous operation itself. The boost::lambda function
90 // object is used as a callback and will update the ec variable when the
91 // operation completes. The blocking_udp_client.cpp example shows how you
92 // can use boost::bind rather than boost::lambda.
b32b8144 93 boost::asio::async_connect(socket_, endpoints, var(ec) = _1);
7c673cae
FG
94
95 // Block until the asynchronous operation has completed.
b32b8144 96 do io_context_.run_one(); while (ec == boost::asio::error::would_block);
7c673cae
FG
97
98 // Determine whether a connection was successfully established. The
99 // deadline actor may have had a chance to run and close our socket, even
100 // though the connect operation notionally succeeded. Therefore we must
101 // check whether the socket is still open before deciding if we succeeded
102 // or failed.
103 if (ec || !socket_.is_open())
104 throw boost::system::system_error(
105 ec ? ec : boost::asio::error::operation_aborted);
106 }
107
108 std::string read_line(boost::posix_time::time_duration timeout)
109 {
110 // Set a deadline for the asynchronous operation. Since this function uses
111 // a composed operation (async_read_until), the deadline applies to the
112 // entire operation, rather than individual reads from the socket.
113 deadline_.expires_from_now(timeout);
114
115 // Set up the variable that receives the result of the asynchronous
116 // operation. The error code is set to would_block to signal that the
117 // operation is incomplete. Asio guarantees that its asynchronous
118 // operations will never fail with would_block, so any other value in
119 // ec indicates completion.
120 boost::system::error_code ec = boost::asio::error::would_block;
121
122 // Start the asynchronous operation itself. The boost::lambda function
123 // object is used as a callback and will update the ec variable when the
124 // operation completes. The blocking_udp_client.cpp example shows how you
125 // can use boost::bind rather than boost::lambda.
126 boost::asio::async_read_until(socket_, input_buffer_, '\n', var(ec) = _1);
127
128 // Block until the asynchronous operation has completed.
b32b8144 129 do io_context_.run_one(); while (ec == boost::asio::error::would_block);
7c673cae
FG
130
131 if (ec)
132 throw boost::system::system_error(ec);
133
134 std::string line;
135 std::istream is(&input_buffer_);
136 std::getline(is, line);
137 return line;
138 }
139
140 void write_line(const std::string& line,
141 boost::posix_time::time_duration timeout)
142 {
143 std::string data = line + "\n";
144
145 // Set a deadline for the asynchronous operation. Since this function uses
146 // a composed operation (async_write), the deadline applies to the entire
147 // operation, rather than individual writes to the socket.
148 deadline_.expires_from_now(timeout);
149
150 // Set up the variable that receives the result of the asynchronous
151 // operation. The error code is set to would_block to signal that the
152 // operation is incomplete. Asio guarantees that its asynchronous
153 // operations will never fail with would_block, so any other value in
154 // ec indicates completion.
155 boost::system::error_code ec = boost::asio::error::would_block;
156
157 // Start the asynchronous operation itself. The boost::lambda function
158 // object is used as a callback and will update the ec variable when the
159 // operation completes. The blocking_udp_client.cpp example shows how you
160 // can use boost::bind rather than boost::lambda.
161 boost::asio::async_write(socket_, boost::asio::buffer(data), var(ec) = _1);
162
163 // Block until the asynchronous operation has completed.
b32b8144 164 do io_context_.run_one(); while (ec == boost::asio::error::would_block);
7c673cae
FG
165
166 if (ec)
167 throw boost::system::system_error(ec);
168 }
169
170private:
171 void check_deadline()
172 {
173 // Check whether the deadline has passed. We compare the deadline against
174 // the current time since a new asynchronous operation may have moved the
175 // deadline before this actor had a chance to run.
176 if (deadline_.expires_at() <= deadline_timer::traits_type::now())
177 {
178 // The deadline has passed. The socket is closed so that any outstanding
179 // asynchronous operations are cancelled. This allows the blocked
180 // connect(), read_line() or write_line() functions to return.
181 boost::system::error_code ignored_ec;
182 socket_.close(ignored_ec);
183
184 // There is no longer an active deadline. The expiry is set to positive
185 // infinity so that the actor takes no action until a new deadline is set.
186 deadline_.expires_at(boost::posix_time::pos_infin);
187 }
188
189 // Put the actor back to sleep.
190 deadline_.async_wait(bind(&client::check_deadline, this));
191 }
192
b32b8144 193 boost::asio::io_context io_context_;
7c673cae
FG
194 tcp::socket socket_;
195 deadline_timer deadline_;
196 boost::asio::streambuf input_buffer_;
197};
198
199//----------------------------------------------------------------------
200
201int main(int argc, char* argv[])
202{
203 try
204 {
205 if (argc != 4)
206 {
207 std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
208 return 1;
209 }
210
211 client c;
212 c.connect(argv[1], argv[2], boost::posix_time::seconds(10));
213
214 boost::posix_time::ptime time_sent =
215 boost::posix_time::microsec_clock::universal_time();
216
217 c.write_line(argv[3], boost::posix_time::seconds(10));
218
219 for (;;)
220 {
221 std::string line = c.read_line(boost::posix_time::seconds(10));
222
223 // Keep going until we get back the line that was sent.
224 if (line == argv[3])
225 break;
226 }
227
228 boost::posix_time::ptime time_received =
229 boost::posix_time::microsec_clock::universal_time();
230
231 std::cout << "Round trip time: ";
232 std::cout << (time_received - time_sent).total_microseconds();
233 std::cout << " microseconds\n";
234 }
235 catch (std::exception& e)
236 {
237 std::cerr << "Exception: " << e.what() << "\n";
238 }
239
240 return 0;
241}