2 // blocking_tcp_client.cpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~
5 // Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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)
11 #include <boost/asio/connect.hpp>
12 #include <boost/asio/deadline_timer.hpp>
13 #include <boost/asio/io_service.hpp>
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>
22 #include <boost/lambda/bind.hpp>
23 #include <boost/lambda/lambda.hpp>
25 using boost::asio::deadline_timer;
26 using boost::asio::ip::tcp;
27 using boost::lambda::bind;
28 using boost::lambda::var;
29 using boost::lambda::_1;
31 //----------------------------------------------------------------------
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
41 // | check_deadline |<---+
43 // +----------------+ | async_wait()
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
51 // io_service to block thread execution until the actor completes.
57 : socket_(io_service_),
58 deadline_(io_service_)
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);
65 // Start the persistent actor that checks for deadline expiry.
69 void connect(const std::string& host, const std::string& service,
70 boost::posix_time::time_duration timeout)
72 // Resolve the host name and service to a list of endpoints.
73 tcp::resolver::query query(host, service);
74 tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
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);
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;
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.
93 boost::asio::async_connect(socket_, iter, var(ec) = _1);
95 // Block until the asynchronous operation has completed.
96 do io_service_.run_one(); while (ec == boost::asio::error::would_block);
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
103 if (ec || !socket_.is_open())
104 throw boost::system::system_error(
105 ec ? ec : boost::asio::error::operation_aborted);
108 std::string read_line(boost::posix_time::time_duration timeout)
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);
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;
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);
128 // Block until the asynchronous operation has completed.
129 do io_service_.run_one(); while (ec == boost::asio::error::would_block);
132 throw boost::system::system_error(ec);
135 std::istream is(&input_buffer_);
136 std::getline(is, line);
140 void write_line(const std::string& line,
141 boost::posix_time::time_duration timeout)
143 std::string data = line + "\n";
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);
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;
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);
163 // Block until the asynchronous operation has completed.
164 do io_service_.run_one(); while (ec == boost::asio::error::would_block);
167 throw boost::system::system_error(ec);
171 void check_deadline()
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())
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);
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);
189 // Put the actor back to sleep.
190 deadline_.async_wait(bind(&client::check_deadline, this));
193 boost::asio::io_service io_service_;
195 deadline_timer deadline_;
196 boost::asio::streambuf input_buffer_;
199 //----------------------------------------------------------------------
201 int main(int argc, char* argv[])
207 std::cerr << "Usage: blocking_tcp <host> <port> <message>\n";
212 c.connect(argv[1], argv[2], boost::posix_time::seconds(10));
214 boost::posix_time::ptime time_sent =
215 boost::posix_time::microsec_clock::universal_time();
217 c.write_line(argv[3], boost::posix_time::seconds(10));
221 std::string line = c.read_line(boost::posix_time::seconds(10));
223 // Keep going until we get back the line that was sent.
228 boost::posix_time::ptime time_received =
229 boost::posix_time::microsec_clock::universal_time();
231 std::cout << "Round trip time: ";
232 std::cout << (time_received - time_sent).total_microseconds();
233 std::cout << " microseconds\n";
235 catch (std::exception& e)
237 std::cerr << "Exception: " << e.what() << "\n";