Imported Upstream version 1.57.0
[platform/upstream/boost.git] / doc / html / boost_asio / example / cpp03 / timeouts / blocking_tcp_client.cpp
1 //
2 // blocking_tcp_client.cpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2014 Christopher M. Kohlhoff (chris at kohlhoff dot com)
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>
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>
19 #include <cstdlib>
20 #include <iostream>
21 #include <string>
22 #include <boost/lambda/bind.hpp>
23 #include <boost/lambda/lambda.hpp>
24
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;
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
51 // io_service to block thread execution until the actor completes.
52 //
53 class client
54 {
55 public:
56   client()
57     : socket_(io_service_),
58       deadline_(io_service_)
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.
73     tcp::resolver::query query(host, service);
74     tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
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.
93     boost::asio::async_connect(socket_, iter, var(ec) = _1);
94
95     // Block until the asynchronous operation has completed.
96     do io_service_.run_one(); while (ec == boost::asio::error::would_block);
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.
129     do io_service_.run_one(); while (ec == boost::asio::error::would_block);
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.
164     do io_service_.run_one(); while (ec == boost::asio::error::would_block);
165
166     if (ec)
167       throw boost::system::system_error(ec);
168   }
169
170 private:
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
193   boost::asio::io_service io_service_;
194   tcp::socket socket_;
195   deadline_timer deadline_;
196   boost::asio::streambuf input_buffer_;
197 };
198
199 //----------------------------------------------------------------------
200
201 int 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 }