summaryrefslogtreecommitdiff
path: root/doc/html/boost_asio/example/cpp11/timeouts/blocking_udp_client.cpp
blob: 395ddd171a67ffa84b0136af28f8ad7850e9a4a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//
// blocking_udp_client.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <boost/asio/buffer.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/udp.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>

using boost::asio::ip::udp;
using std::placeholders::_1;
using std::placeholders::_2;

//----------------------------------------------------------------------

//
// This class manages socket timeouts by running the io_context using the timed
// io_context::run_for() member function. Each asynchronous operation is given
// a timeout within which it must complete. The socket operations themselves
// use std::bind to specify the completion handler:
//
//   +---------------+
//   |               |
//   |    receive    |
//   |               |
//   +---------------+
//           |
//  async_-  |    +----------------+
// receive() |    |                |
//           +--->| handle_receive |
//                |                |
//                +----------------+
//
// For a given socket operation, the client object runs the io_context to block
// thread execution until the operation completes or the timeout is reached. If
// the io_context::run_for() function times out, the socket is closed and the
// outstanding asynchronous operation is cancelled.
//
class client
{
public:
  client(const udp::endpoint& listen_endpoint)
    : socket_(io_context_, listen_endpoint)
  {
  }

  std::size_t receive(const boost::asio::mutable_buffer& buffer,
      std::chrono::steady_clock::duration timeout,
      boost::system::error_code& error)
  {
    // Start the asynchronous operation. The handle_receive function used as a
    // callback will update the error and length variables.
    std::size_t length = 0;
    socket_.async_receive(boost::asio::buffer(buffer),
        std::bind(&client::handle_receive, _1, _2, &error, &length));

    // Run the operation until it completes, or until the timeout.
    run(timeout);

    return length;
  }

private:
  void run(std::chrono::steady_clock::duration timeout)
  {
    // Restart the io_context, as it may have been left in the "stopped" state
    // by a previous operation.
    io_context_.restart();

    // Block until the asynchronous operation has completed, or timed out. If
    // the pending asynchronous operation is a composed operation, the deadline
    // applies to the entire operation, rather than individual operations on
    // the socket.
    io_context_.run_for(timeout);

    // If the asynchronous operation completed successfully then the io_context
    // would have been stopped due to running out of work. If it was not
    // stopped, then the io_context::run_for call must have timed out.
    if (!io_context_.stopped())
    {
      // Cancel the outstanding asynchronous operation.
      socket_.cancel();

      // Run the io_context again until the operation completes.
      io_context_.run();
    }
  }

  static void handle_receive(
      const boost::system::error_code& error, std::size_t length,
      boost::system::error_code* out_error, std::size_t* out_length)
  {
    *out_error = error;
    *out_length = length;
  }

private:
  boost::asio::io_context io_context_;
  udp::socket socket_;
};

//----------------------------------------------------------------------

int main(int argc, char* argv[])
{
  try
  {
    using namespace std; // For atoi.

    if (argc != 3)
    {
      std::cerr << "Usage: blocking_udp_client <listen_addr> <listen_port>\n";
      return 1;
    }

    udp::endpoint listen_endpoint(
        boost::asio::ip::make_address(argv[1]),
        std::atoi(argv[2]));

    client c(listen_endpoint);

    for (;;)
    {
      char data[1024];
      boost::system::error_code error;
      std::size_t n = c.receive(boost::asio::buffer(data),
          std::chrono::seconds(10), error);

      if (error)
      {
        std::cout << "Receive error: " << error.message() << "\n"; 
      }
      else
      {
        std::cout << "Received: ";
        std::cout.write(data, n);
        std::cout << "\n";
      }
    }
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}