]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // blocking_udp_client.cpp | |
3 | // ~~~~~~~~~~~~~~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 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/deadline_timer.hpp> | |
12 | #include <boost/asio/io_service.hpp> | |
13 | #include <boost/asio/ip/udp.hpp> | |
14 | #include <cstdlib> | |
15 | #include <boost/bind.hpp> | |
16 | #include <boost/date_time/posix_time/posix_time_types.hpp> | |
17 | #include <iostream> | |
18 | ||
19 | using boost::asio::deadline_timer; | |
20 | using boost::asio::ip::udp; | |
21 | ||
22 | //---------------------------------------------------------------------- | |
23 | ||
24 | // | |
25 | // This class manages socket timeouts by applying the concept of a deadline. | |
26 | // Each asynchronous operation is given a deadline by which it must complete. | |
27 | // Deadlines are enforced by an "actor" that persists for the lifetime of the | |
28 | // client object: | |
29 | // | |
30 | // +----------------+ | |
31 | // | | | |
32 | // | check_deadline |<---+ | |
33 | // | | | | |
34 | // +----------------+ | async_wait() | |
35 | // | | | |
36 | // +---------+ | |
37 | // | |
38 | // If the actor determines that the deadline has expired, any outstanding | |
39 | // socket operations are cancelled. The socket operations themselves are | |
40 | // implemented as transient actors: | |
41 | // | |
42 | // +---------------+ | |
43 | // | | | |
44 | // | receive | | |
45 | // | | | |
46 | // +---------------+ | |
47 | // | | |
48 | // async_- | +----------------+ | |
49 | // receive() | | | | |
50 | // +--->| handle_receive | | |
51 | // | | | |
52 | // +----------------+ | |
53 | // | |
54 | // The client object runs the io_service to block thread execution until the | |
55 | // actor completes. | |
56 | // | |
57 | class client | |
58 | { | |
59 | public: | |
60 | client(const udp::endpoint& listen_endpoint) | |
61 | : socket_(io_service_, listen_endpoint), | |
62 | deadline_(io_service_) | |
63 | { | |
64 | // No deadline is required until the first socket operation is started. We | |
65 | // set the deadline to positive infinity so that the actor takes no action | |
66 | // until a specific deadline is set. | |
67 | deadline_.expires_at(boost::posix_time::pos_infin); | |
68 | ||
69 | // Start the persistent actor that checks for deadline expiry. | |
70 | check_deadline(); | |
71 | } | |
72 | ||
73 | std::size_t receive(const boost::asio::mutable_buffer& buffer, | |
74 | boost::posix_time::time_duration timeout, boost::system::error_code& ec) | |
75 | { | |
76 | // Set a deadline for the asynchronous operation. | |
77 | deadline_.expires_from_now(timeout); | |
78 | ||
79 | // Set up the variables that receive the result of the asynchronous | |
80 | // operation. The error code is set to would_block to signal that the | |
81 | // operation is incomplete. Asio guarantees that its asynchronous | |
82 | // operations will never fail with would_block, so any other value in | |
83 | // ec indicates completion. | |
84 | ec = boost::asio::error::would_block; | |
85 | std::size_t length = 0; | |
86 | ||
87 | // Start the asynchronous operation itself. The handle_receive function | |
88 | // used as a callback will update the ec and length variables. | |
89 | socket_.async_receive(boost::asio::buffer(buffer), | |
90 | boost::bind(&client::handle_receive, _1, _2, &ec, &length)); | |
91 | ||
92 | // Block until the asynchronous operation has completed. | |
93 | do io_service_.run_one(); while (ec == boost::asio::error::would_block); | |
94 | ||
95 | return length; | |
96 | } | |
97 | ||
98 | private: | |
99 | void check_deadline() | |
100 | { | |
101 | // Check whether the deadline has passed. We compare the deadline against | |
102 | // the current time since a new asynchronous operation may have moved the | |
103 | // deadline before this actor had a chance to run. | |
104 | if (deadline_.expires_at() <= deadline_timer::traits_type::now()) | |
105 | { | |
106 | // The deadline has passed. The outstanding asynchronous operation needs | |
107 | // to be cancelled so that the blocked receive() function will return. | |
108 | // | |
109 | // Please note that cancel() has portability issues on some versions of | |
110 | // Microsoft Windows, and it may be necessary to use close() instead. | |
111 | // Consult the documentation for cancel() for further information. | |
112 | socket_.cancel(); | |
113 | ||
114 | // There is no longer an active deadline. The expiry is set to positive | |
115 | // infinity so that the actor takes no action until a new deadline is set. | |
116 | deadline_.expires_at(boost::posix_time::pos_infin); | |
117 | } | |
118 | ||
119 | // Put the actor back to sleep. | |
120 | deadline_.async_wait(boost::bind(&client::check_deadline, this)); | |
121 | } | |
122 | ||
123 | static void handle_receive( | |
124 | const boost::system::error_code& ec, std::size_t length, | |
125 | boost::system::error_code* out_ec, std::size_t* out_length) | |
126 | { | |
127 | *out_ec = ec; | |
128 | *out_length = length; | |
129 | } | |
130 | ||
131 | private: | |
132 | boost::asio::io_service io_service_; | |
133 | udp::socket socket_; | |
134 | deadline_timer deadline_; | |
135 | }; | |
136 | ||
137 | //---------------------------------------------------------------------- | |
138 | ||
139 | int main(int argc, char* argv[]) | |
140 | { | |
141 | try | |
142 | { | |
143 | using namespace std; // For atoi. | |
144 | ||
145 | if (argc != 3) | |
146 | { | |
147 | std::cerr << "Usage: blocking_udp_timeout <listen_addr> <listen_port>\n"; | |
148 | return 1; | |
149 | } | |
150 | ||
151 | udp::endpoint listen_endpoint( | |
152 | boost::asio::ip::address::from_string(argv[1]), | |
153 | std::atoi(argv[2])); | |
154 | ||
155 | client c(listen_endpoint); | |
156 | ||
157 | for (;;) | |
158 | { | |
159 | char data[1024]; | |
160 | boost::system::error_code ec; | |
161 | std::size_t n = c.receive(boost::asio::buffer(data), | |
162 | boost::posix_time::seconds(10), ec); | |
163 | ||
164 | if (ec) | |
165 | { | |
166 | std::cout << "Receive error: " << ec.message() << "\n"; | |
167 | } | |
168 | else | |
169 | { | |
170 | std::cout << "Received: "; | |
171 | std::cout.write(data, n); | |
172 | std::cout << "\n"; | |
173 | } | |
174 | } | |
175 | } | |
176 | catch (std::exception& e) | |
177 | { | |
178 | std::cerr << "Exception: " << e.what() << "\n"; | |
179 | } | |
180 | ||
181 | return 0; | |
182 | } |