1 //
2 // blocking_udp_client.cpp
3 // ~~~~~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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/buffer.hpp>
12 #include <boost/asio/io_context.hpp>
13 #include <boost/asio/ip/udp.hpp>
14 #include <cstdlib>
15 #include <functional>
16 #include <iostream>
17
18 using boost::asio::ip::udp;
19 using std::placeholders::_1;
20 using std::placeholders::_2;
21
22 //----------------------------------------------------------------------
23
24 //
25 // This class manages socket timeouts by running the io_context using the timed
26 // io_context::run_for() member function. Each asynchronous operation is given
27 // a timeout within which it must complete. The socket operations themselves
28 // use std::bind to specify the completion handler:
29 //
30 // +---------------+
31 // | |
32 // | receive |
33 // | |
34 // +---------------+
35 // |
36 // async_- | +----------------+
37 // receive() | | |
38 // +--->| handle_receive |
39 // | |
40 // +----------------+
41 //
42 // For a given socket operation, the client object runs the io_context to block
43 // thread execution until the operation completes or the timeout is reached. If
44 // the io_context::run_for() function times out, the socket is closed and the
45 // outstanding asynchronous operation is cancelled.
46 //
47 class client
48 {
49 public:
client(const udp::endpoint & listen_endpoint)50 client(const udp::endpoint& listen_endpoint)
51 : socket_(io_context_, listen_endpoint)
52 {
53 }
54
receive(const boost::asio::mutable_buffer & buffer,std::chrono::steady_clock::duration timeout,boost::system::error_code & error)55 std::size_t receive(const boost::asio::mutable_buffer& buffer,
56 std::chrono::steady_clock::duration timeout,
57 boost::system::error_code& error)
58 {
59 // Start the asynchronous operation. The handle_receive function used as a
60 // callback will update the error and length variables.
61 std::size_t length = 0;
62 socket_.async_receive(boost::asio::buffer(buffer),
63 std::bind(&client::handle_receive, _1, _2, &error, &length));
64
65 // Run the operation until it completes, or until the timeout.
66 run(timeout);
67
68 return length;
69 }
70
71 private:
run(std::chrono::steady_clock::duration timeout)72 void run(std::chrono::steady_clock::duration timeout)
73 {
74 // Restart the io_context, as it may have been left in the "stopped" state
75 // by a previous operation.
76 io_context_.restart();
77
78 // Block until the asynchronous operation has completed, or timed out. If
79 // the pending asynchronous operation is a composed operation, the deadline
80 // applies to the entire operation, rather than individual operations on
81 // the socket.
82 io_context_.run_for(timeout);
83
84 // If the asynchronous operation completed successfully then the io_context
85 // would have been stopped due to running out of work. If it was not
86 // stopped, then the io_context::run_for call must have timed out.
87 if (!io_context_.stopped())
88 {
89 // Cancel the outstanding asynchronous operation.
90 socket_.cancel();
91
92 // Run the io_context again until the operation completes.
93 io_context_.run();
94 }
95 }
96
handle_receive(const boost::system::error_code & error,std::size_t length,boost::system::error_code * out_error,std::size_t * out_length)97 static void handle_receive(
98 const boost::system::error_code& error, std::size_t length,
99 boost::system::error_code* out_error, std::size_t* out_length)
100 {
101 *out_error = error;
102 *out_length = length;
103 }
104
105 private:
106 boost::asio::io_context io_context_;
107 udp::socket socket_;
108 };
109
110 //----------------------------------------------------------------------
111
main(int argc,char * argv[])112 int main(int argc, char* argv[])
113 {
114 try
115 {
116 using namespace std; // For atoi.
117
118 if (argc != 3)
119 {
120 std::cerr << "Usage: blocking_udp_client <listen_addr> <listen_port>\n";
121 return 1;
122 }
123
124 udp::endpoint listen_endpoint(
125 boost::asio::ip::make_address(argv[1]),
126 std::atoi(argv[2]));
127
128 client c(listen_endpoint);
129
130 for (;;)
131 {
132 char data[1024];
133 boost::system::error_code error;
134 std::size_t n = c.receive(boost::asio::buffer(data),
135 std::chrono::seconds(10), error);
136
137 if (error)
138 {
139 std::cout << "Receive error: " << error.message() << "\n";
140 }
141 else
142 {
143 std::cout << "Received: ";
144 std::cout.write(data, n);
145 std::cout << "\n";
146 }
147 }
148 }
149 catch (std::exception& e)
150 {
151 std::cerr << "Exception: " << e.what() << "\n";
152 }
153
154 return 0;
155 }
156