100.00% Lines (94/94) 100.00% Functions (29/29)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
  3 + // Copyright (c) 2026 Michael Vandeberg
3   // 4   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 5   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 7   //
7   // Official repository: https://github.com/cppalliance/corosio 8   // Official repository: https://github.com/cppalliance/corosio
8   // 9   //
9   10  
10   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP 11   #ifndef BOOST_COROSIO_UDP_SOCKET_HPP
11   #define BOOST_COROSIO_UDP_SOCKET_HPP 12   #define BOOST_COROSIO_UDP_SOCKET_HPP
12   13  
13   #include <boost/corosio/detail/config.hpp> 14   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/detail/platform.hpp> 15   #include <boost/corosio/detail/platform.hpp>
15   #include <boost/corosio/detail/except.hpp> 16   #include <boost/corosio/detail/except.hpp>
16   #include <boost/corosio/detail/native_handle.hpp> 17   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 18   #include <boost/corosio/detail/op_base.hpp>
18   #include <boost/corosio/io/io_object.hpp> 19   #include <boost/corosio/io/io_object.hpp>
19   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
20   #include <boost/corosio/detail/buffer_param.hpp> 21   #include <boost/corosio/detail/buffer_param.hpp>
21   #include <boost/corosio/endpoint.hpp> 22   #include <boost/corosio/endpoint.hpp>
22   #include <boost/corosio/message_flags.hpp> 23   #include <boost/corosio/message_flags.hpp>
23   #include <boost/corosio/shutdown_type.hpp> 24   #include <boost/corosio/shutdown_type.hpp>
24   #include <boost/corosio/udp.hpp> 25   #include <boost/corosio/udp.hpp>
25   #include <boost/corosio/wait_type.hpp> 26   #include <boost/corosio/wait_type.hpp>
26   #include <boost/capy/ex/executor_ref.hpp> 27   #include <boost/capy/ex/executor_ref.hpp>
27   #include <boost/capy/ex/execution_context.hpp> 28   #include <boost/capy/ex/execution_context.hpp>
28   #include <boost/capy/ex/io_env.hpp> 29   #include <boost/capy/ex/io_env.hpp>
29   #include <boost/capy/concept/executor.hpp> 30   #include <boost/capy/concept/executor.hpp>
30   31  
31   #include <system_error> 32   #include <system_error>
32   33  
33   #include <concepts> 34   #include <concepts>
34   #include <coroutine> 35   #include <coroutine>
35   #include <cstddef> 36   #include <cstddef>
36   #include <stop_token> 37   #include <stop_token>
37   #include <type_traits> 38   #include <type_traits>
38   39  
39   namespace boost::corosio { 40   namespace boost::corosio {
40   41  
41   /** An asynchronous UDP socket for coroutine I/O. 42   /** An asynchronous UDP socket for coroutine I/O.
42   43  
43   This class provides asynchronous UDP datagram operations that 44   This class provides asynchronous UDP datagram operations that
44   return awaitable types. Each operation participates in the affine 45   return awaitable types. Each operation participates in the affine
45   awaitable protocol, ensuring coroutines resume on the correct 46   awaitable protocol, ensuring coroutines resume on the correct
46   executor. 47   executor.
47   48  
48   Supports two modes of operation: 49   Supports two modes of operation:
49   50  
50   **Connectionless mode**: each `send_to` specifies a destination 51   **Connectionless mode**: each `send_to` specifies a destination
51   endpoint, and each `recv_from` captures the source endpoint. 52   endpoint, and each `recv_from` captures the source endpoint.
52   The socket must be opened (and optionally bound) before I/O. 53   The socket must be opened (and optionally bound) before I/O.
53   54  
54   **Connected mode**: call `connect()` to set a default peer, 55   **Connected mode**: call `connect()` to set a default peer,
55   then use `send()`/`recv()` without endpoint arguments. 56   then use `send()`/`recv()` without endpoint arguments.
56   The kernel filters incoming datagrams to those from the 57   The kernel filters incoming datagrams to those from the
57   connected peer. 58   connected peer.
58   59  
59   @par Thread Safety 60   @par Thread Safety
60   Distinct objects: Safe.@n 61   Distinct objects: Safe.@n
61   Shared objects: Unsafe. A socket must not have concurrent 62   Shared objects: Unsafe. A socket must not have concurrent
62   operations of the same type (e.g., two simultaneous recv_from). 63   operations of the same type (e.g., two simultaneous recv_from).
63   One send_to and one recv_from may be in flight simultaneously. 64   One send_to and one recv_from may be in flight simultaneously.
64   65  
65   @par Example 66   @par Example
66 - @code 67 + @par !example udp_socket
67 - // Connectionless mode  
68 - io_context ioc;  
69 - udp_socket sock( ioc );  
70 - if ( auto ec = sock.open( udp::v4() ) )  
71 - co_return;  
72 - if ( auto ec = sock.bind( endpoint( ipv4_address::any(), 9000 ) ) )  
73 - co_return;  
74 -  
75 - char buf[1024];  
76 - endpoint sender;  
77 - auto [ec, n] = co_await sock.recv_from(  
78 - capy::mutable_buffer( buf, sizeof( buf ) ), sender );  
79 - if ( ec )  
80 - co_return;  
81 - auto [sec, sn] = co_await sock.send_to(  
82 - capy::const_buffer( buf, n ), sender );  
83 - if ( sec )  
84 - co_return;  
85 -  
86 - // Connected mode  
87 - udp_socket csock( ioc );  
88 - auto [cec] = co_await csock.connect(  
89 - endpoint( ipv4_address::loopback(), 9000 ) );  
90 - if ( cec )  
91 - co_return;  
92 - auto [wec, wn] = co_await csock.send(  
93 - capy::const_buffer( buf, n ) );  
94 - if ( wec )  
95 - co_return;  
96 - @endcode  
97   */ 68   */
98   class BOOST_COROSIO_DECL udp_socket : public io_object 69   class BOOST_COROSIO_DECL udp_socket : public io_object
99   { 70   {
100   public: 71   public:
101   using shutdown_type = corosio::shutdown_type; 72   using shutdown_type = corosio::shutdown_type;
102   using enum corosio::shutdown_type; 73   using enum corosio::shutdown_type;
103   74  
104   /** Define backend hooks for UDP socket operations. 75   /** Define backend hooks for UDP socket operations.
105   76  
106   Platform backends (epoll, kqueue, select) derive from 77   Platform backends (epoll, kqueue, select) derive from
107   this to implement datagram I/O and option management. 78   this to implement datagram I/O and option management.
108   */ 79   */
109   struct implementation : io_object::implementation 80   struct implementation : io_object::implementation
110   { 81   {
111   /** Initiate an asynchronous send_to operation. 82   /** Initiate an asynchronous send_to operation.
112   83  
113   @param h Coroutine handle to resume on completion. 84   @param h Coroutine handle to resume on completion.
114   @param ex Executor for dispatching the completion. 85   @param ex Executor for dispatching the completion.
115   @param buf The buffer data to send. 86   @param buf The buffer data to send.
116   @param dest The destination endpoint. 87   @param dest The destination endpoint.
117   @param flags Platform message flags (e.g. `MSG_DONTWAIT`). 88   @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
118   @param token Stop token for cancellation. 89   @param token Stop token for cancellation.
119   @param ec Output error code. 90   @param ec Output error code.
120   @param bytes_out Output bytes transferred. 91   @param bytes_out Output bytes transferred.
121   92  
122   @return Coroutine handle to resume immediately. 93   @return Coroutine handle to resume immediately.
123   */ 94   */
124   virtual std::coroutine_handle<> send_to( 95   virtual std::coroutine_handle<> send_to(
125   std::coroutine_handle<> h, 96   std::coroutine_handle<> h,
126   capy::executor_ref ex, 97   capy::executor_ref ex,
127   buffer_param buf, 98   buffer_param buf,
128   endpoint dest, 99   endpoint dest,
129   int flags, 100   int flags,
130   std::stop_token token, 101   std::stop_token token,
131   std::error_code* ec, 102   std::error_code* ec,
132   std::size_t* bytes_out) = 0; 103   std::size_t* bytes_out) = 0;
133   104  
134   /** Initiate an asynchronous recv_from operation. 105   /** Initiate an asynchronous recv_from operation.
135   106  
136   @param h Coroutine handle to resume on completion. 107   @param h Coroutine handle to resume on completion.
137   @param ex Executor for dispatching the completion. 108   @param ex Executor for dispatching the completion.
138   @param buf The buffer to receive into. 109   @param buf The buffer to receive into.
139   @param source Output endpoint for the sender's address. 110   @param source Output endpoint for the sender's address.
140   @param flags Platform message flags (e.g. `MSG_PEEK`). 111   @param flags Platform message flags (e.g. `MSG_PEEK`).
141   @param token Stop token for cancellation. 112   @param token Stop token for cancellation.
142   @param ec Output error code. 113   @param ec Output error code.
143   @param bytes_out Output bytes transferred. 114   @param bytes_out Output bytes transferred.
144   115  
145   @return Coroutine handle to resume immediately. 116   @return Coroutine handle to resume immediately.
146   */ 117   */
147   virtual std::coroutine_handle<> recv_from( 118   virtual std::coroutine_handle<> recv_from(
148   std::coroutine_handle<> h, 119   std::coroutine_handle<> h,
149   capy::executor_ref ex, 120   capy::executor_ref ex,
150   buffer_param buf, 121   buffer_param buf,
151   endpoint* source, 122   endpoint* source,
152   int flags, 123   int flags,
153   std::stop_token token, 124   std::stop_token token,
154   std::error_code* ec, 125   std::error_code* ec,
155   std::size_t* bytes_out) = 0; 126   std::size_t* bytes_out) = 0;
156   127  
157   /// Return the platform socket descriptor. 128   /// Return the platform socket descriptor.
158   virtual native_handle_type native_handle() const noexcept = 0; 129   virtual native_handle_type native_handle() const noexcept = 0;
159   130  
160   /** Release ownership of the native socket handle. 131   /** Release ownership of the native socket handle.
161   132  
162   Deregisters the socket from the backend and cancels 133   Deregisters the socket from the backend and cancels
163   pending operations without closing the descriptor. The 134   pending operations without closing the descriptor. The
164   caller takes ownership. 135   caller takes ownership.
165   136  
166   @return The native handle. 137   @return The native handle.
167   */ 138   */
168   virtual native_handle_type release_socket() noexcept = 0; 139   virtual native_handle_type release_socket() noexcept = 0;
169   140  
170   /** Request cancellation of pending asynchronous operations. 141   /** Request cancellation of pending asynchronous operations.
171   142  
172   All outstanding operations complete with operation_canceled 143   All outstanding operations complete with operation_canceled
173   error. Check `ec == cond::canceled` for portable comparison. 144   error. Check `ec == cond::canceled` for portable comparison.
174   */ 145   */
175   virtual void cancel() noexcept = 0; 146   virtual void cancel() noexcept = 0;
176   147  
177   /// Shut down the socket in one or both directions. 148   /// Shut down the socket in one or both directions.
178   virtual std::error_code shutdown(shutdown_type what) noexcept = 0; 149   virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
179   150  
180   /** Set a socket option. 151   /** Set a socket option.
181   152  
182   @param level The protocol level (e.g. `SOL_SOCKET`). 153   @param level The protocol level (e.g. `SOL_SOCKET`).
183   @param optname The option name. 154   @param optname The option name.
184   @param data Pointer to the option value. 155   @param data Pointer to the option value.
185   @param size Size of the option value in bytes. 156   @param size Size of the option value in bytes.
186   @return Error code on failure, empty on success. 157   @return Error code on failure, empty on success.
187   */ 158   */
188   virtual std::error_code set_option( 159   virtual std::error_code set_option(
189   int level, 160   int level,
190   int optname, 161   int optname,
191   void const* data, 162   void const* data,
192   std::size_t size) noexcept = 0; 163   std::size_t size) noexcept = 0;
193   164  
194   /** Get a socket option. 165   /** Get a socket option.
195   166  
196   @param level The protocol level (e.g. `SOL_SOCKET`). 167   @param level The protocol level (e.g. `SOL_SOCKET`).
197   @param optname The option name. 168   @param optname The option name.
198   @param data Pointer to receive the option value. 169   @param data Pointer to receive the option value.
199   @param size On entry, the size of the buffer. On exit, 170   @param size On entry, the size of the buffer. On exit,
200   the size of the option value. 171   the size of the option value.
201   @return Error code on failure, empty on success. 172   @return Error code on failure, empty on success.
202   */ 173   */
203   virtual std::error_code 174   virtual std::error_code
204   get_option(int level, int optname, void* data, std::size_t* size) 175   get_option(int level, int optname, void* data, std::size_t* size)
205   const noexcept = 0; 176   const noexcept = 0;
206   177  
207   /// Return the cached local endpoint. 178   /// Return the cached local endpoint.
208   virtual endpoint local_endpoint() const noexcept = 0; 179   virtual endpoint local_endpoint() const noexcept = 0;
209   180  
210   /// Return the cached remote endpoint (connected mode). 181   /// Return the cached remote endpoint (connected mode).
211   virtual endpoint remote_endpoint() const noexcept = 0; 182   virtual endpoint remote_endpoint() const noexcept = 0;
212   183  
213   /** Initiate an asynchronous connect to set the default peer. 184   /** Initiate an asynchronous connect to set the default peer.
214   185  
215   @param h Coroutine handle to resume on completion. 186   @param h Coroutine handle to resume on completion.
216   @param ex Executor for dispatching the completion. 187   @param ex Executor for dispatching the completion.
217   @param ep The remote endpoint to connect to. 188   @param ep The remote endpoint to connect to.
218   @param token Stop token for cancellation. 189   @param token Stop token for cancellation.
219   @param ec Output error code. 190   @param ec Output error code.
220   191  
221   @return Coroutine handle to resume immediately. 192   @return Coroutine handle to resume immediately.
222   */ 193   */
223   virtual std::coroutine_handle<> connect( 194   virtual std::coroutine_handle<> connect(
224   std::coroutine_handle<> h, 195   std::coroutine_handle<> h,
225   capy::executor_ref ex, 196   capy::executor_ref ex,
226   endpoint ep, 197   endpoint ep,
227   std::stop_token token, 198   std::stop_token token,
228   std::error_code* ec) = 0; 199   std::error_code* ec) = 0;
229   200  
230   /** Initiate an asynchronous connected send operation. 201   /** Initiate an asynchronous connected send operation.
231   202  
232   @param h Coroutine handle to resume on completion. 203   @param h Coroutine handle to resume on completion.
233   @param ex Executor for dispatching the completion. 204   @param ex Executor for dispatching the completion.
234   @param buf The buffer data to send. 205   @param buf The buffer data to send.
235   @param flags Platform message flags (e.g. `MSG_DONTWAIT`). 206   @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
236   @param token Stop token for cancellation. 207   @param token Stop token for cancellation.
237   @param ec Output error code. 208   @param ec Output error code.
238   @param bytes_out Output bytes transferred. 209   @param bytes_out Output bytes transferred.
239   210  
240   @return Coroutine handle to resume immediately. 211   @return Coroutine handle to resume immediately.
241   */ 212   */
242   virtual std::coroutine_handle<> send( 213   virtual std::coroutine_handle<> send(
243   std::coroutine_handle<> h, 214   std::coroutine_handle<> h,
244   capy::executor_ref ex, 215   capy::executor_ref ex,
245   buffer_param buf, 216   buffer_param buf,
246   int flags, 217   int flags,
247   std::stop_token token, 218   std::stop_token token,
248   std::error_code* ec, 219   std::error_code* ec,
249   std::size_t* bytes_out) = 0; 220   std::size_t* bytes_out) = 0;
250   221  
251   /** Initiate an asynchronous connected recv operation. 222   /** Initiate an asynchronous connected recv operation.
252   223  
253   @param h Coroutine handle to resume on completion. 224   @param h Coroutine handle to resume on completion.
254   @param ex Executor for dispatching the completion. 225   @param ex Executor for dispatching the completion.
255   @param buf The buffer to receive into. 226   @param buf The buffer to receive into.
256   @param flags Platform message flags (e.g. `MSG_PEEK`). 227   @param flags Platform message flags (e.g. `MSG_PEEK`).
257   @param token Stop token for cancellation. 228   @param token Stop token for cancellation.
258   @param ec Output error code. 229   @param ec Output error code.
259   @param bytes_out Output bytes transferred. 230   @param bytes_out Output bytes transferred.
260   231  
261   @return Coroutine handle to resume immediately. 232   @return Coroutine handle to resume immediately.
262   */ 233   */
263   virtual std::coroutine_handle<> recv( 234   virtual std::coroutine_handle<> recv(
264   std::coroutine_handle<> h, 235   std::coroutine_handle<> h,
265   capy::executor_ref ex, 236   capy::executor_ref ex,
266   buffer_param buf, 237   buffer_param buf,
267   int flags, 238   int flags,
268   std::stop_token token, 239   std::stop_token token,
269   std::error_code* ec, 240   std::error_code* ec,
270   std::size_t* bytes_out) = 0; 241   std::size_t* bytes_out) = 0;
271   242  
272   /** Initiate an asynchronous wait for socket readiness. 243   /** Initiate an asynchronous wait for socket readiness.
273   244  
274   Completes when the socket becomes ready for the 245   Completes when the socket becomes ready for the
275   specified direction, or an error condition is 246   specified direction, or an error condition is
276   reported. No bytes are transferred. 247   reported. No bytes are transferred.
277   248  
278   @param h Coroutine handle to resume on completion. 249   @param h Coroutine handle to resume on completion.
279   @param ex Executor for dispatching the completion. 250   @param ex Executor for dispatching the completion.
280   @param w The direction to wait on. 251   @param w The direction to wait on.
281   @param token Stop token for cancellation. 252   @param token Stop token for cancellation.
282   @param ec Output error code. 253   @param ec Output error code.
283   254  
284   @return Coroutine handle to resume immediately. 255   @return Coroutine handle to resume immediately.
285   */ 256   */
286   virtual std::coroutine_handle<> wait( 257   virtual std::coroutine_handle<> wait(
287   std::coroutine_handle<> h, 258   std::coroutine_handle<> h,
288   capy::executor_ref ex, 259   capy::executor_ref ex,
289   wait_type w, 260   wait_type w,
290   std::stop_token token, 261   std::stop_token token,
291   std::error_code* ec) = 0; 262   std::error_code* ec) = 0;
292   }; 263   };
293   264  
294   /** Represent the awaitable returned by @ref send_to. 265   /** Represent the awaitable returned by @ref send_to.
295   266  
296   Captures the destination endpoint and buffer, then dispatches 267   Captures the destination endpoint and buffer, then dispatches
297   to the backend implementation on suspension. 268   to the backend implementation on suspension.
298   */ 269   */
299   struct send_to_awaitable 270   struct send_to_awaitable
300   : detail::bytes_op_base<send_to_awaitable> 271   : detail::bytes_op_base<send_to_awaitable>
301   { 272   {
302   udp_socket& s_; 273   udp_socket& s_;
303   buffer_param buf_; 274   buffer_param buf_;
304   endpoint dest_; 275   endpoint dest_;
305   int flags_; 276   int flags_;
306   277  
HITCBC 307   71 send_to_awaitable( 278   71 send_to_awaitable(
308   udp_socket& s, buffer_param buf, 279   udp_socket& s, buffer_param buf,
309   endpoint dest, int flags = 0) noexcept 280   endpoint dest, int flags = 0) noexcept
HITCBC 310   71 : s_(s), buf_(buf), dest_(dest), flags_(flags) {} 281   71 : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
311   282  
HITCBC 312   69 std::coroutine_handle<> dispatch( 283   69 std::coroutine_handle<> dispatch(
313   std::coroutine_handle<> h, capy::executor_ref ex) const 284   std::coroutine_handle<> h, capy::executor_ref ex) const
314   { 285   {
HITCBC 315   138 return s_.get().send_to( 286   138 return s_.get().send_to(
HITCBC 316   138 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_); 287   138 h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
317   } 288   }
318   }; 289   };
319   290  
320   /** Represent the awaitable returned by @ref recv_from. 291   /** Represent the awaitable returned by @ref recv_from.
321   292  
322   Captures the source endpoint reference and buffer, then 293   Captures the source endpoint reference and buffer, then
323   dispatches to the backend implementation on suspension. 294   dispatches to the backend implementation on suspension.
324   */ 295   */
325   struct recv_from_awaitable 296   struct recv_from_awaitable
326   : detail::bytes_op_base<recv_from_awaitable> 297   : detail::bytes_op_base<recv_from_awaitable>
327   { 298   {
328   udp_socket& s_; 299   udp_socket& s_;
329   buffer_param buf_; 300   buffer_param buf_;
330   endpoint& source_; 301   endpoint& source_;
331   int flags_; 302   int flags_;
332   303  
HITCBC 333   91 recv_from_awaitable( 304   91 recv_from_awaitable(
334   udp_socket& s, buffer_param buf, 305   udp_socket& s, buffer_param buf,
335   endpoint& source, int flags = 0) noexcept 306   endpoint& source, int flags = 0) noexcept
HITCBC 336   91 : s_(s), buf_(buf), source_(source), flags_(flags) {} 307   91 : s_(s), buf_(buf), source_(source), flags_(flags) {}
337   308  
HITCBC 338   89 std::coroutine_handle<> dispatch( 309   89 std::coroutine_handle<> dispatch(
339   std::coroutine_handle<> h, capy::executor_ref ex) const 310   std::coroutine_handle<> h, capy::executor_ref ex) const
340   { 311   {
HITCBC 341   178 return s_.get().recv_from( 312   178 return s_.get().recv_from(
HITCBC 342   178 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_); 313   178 h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
343   } 314   }
344   }; 315   };
345   316  
346   /// Represent the awaitable returned by @ref connect. 317   /// Represent the awaitable returned by @ref connect.
347   struct connect_awaitable 318   struct connect_awaitable
348   : detail::void_op_base<connect_awaitable> 319   : detail::void_op_base<connect_awaitable>
349   { 320   {
350   udp_socket& s_; 321   udp_socket& s_;
351   endpoint endpoint_; 322   endpoint endpoint_;
352   323  
HITCBC 353   40 connect_awaitable(udp_socket& s, endpoint ep) noexcept 324   40 connect_awaitable(udp_socket& s, endpoint ep) noexcept
HITCBC 354   40 : s_(s), endpoint_(ep) {} 325   40 : s_(s), endpoint_(ep) {}
355   326  
HITCBC 356   40 std::coroutine_handle<> dispatch( 327   40 std::coroutine_handle<> dispatch(
357   std::coroutine_handle<> h, capy::executor_ref ex) const 328   std::coroutine_handle<> h, capy::executor_ref ex) const
358   { 329   {
HITCBC 359   40 return s_.get().connect(h, ex, endpoint_, token_, &ec_); 330   40 return s_.get().connect(h, ex, endpoint_, token_, &ec_);
360   } 331   }
361   }; 332   };
362   333  
363   /// Represent the awaitable returned by @ref wait. 334   /// Represent the awaitable returned by @ref wait.
364   struct wait_awaitable 335   struct wait_awaitable
365   : detail::void_op_base<wait_awaitable> 336   : detail::void_op_base<wait_awaitable>
366   { 337   {
367   udp_socket& s_; 338   udp_socket& s_;
368   wait_type w_; 339   wait_type w_;
369   340  
HITCBC 370   30 wait_awaitable(udp_socket& s, wait_type w) noexcept 341   30 wait_awaitable(udp_socket& s, wait_type w) noexcept
HITCBC 371   30 : s_(s), w_(w) {} 342   30 : s_(s), w_(w) {}
372   343  
HITCBC 373   30 std::coroutine_handle<> dispatch( 344   30 std::coroutine_handle<> dispatch(
374   std::coroutine_handle<> h, capy::executor_ref ex) const 345   std::coroutine_handle<> h, capy::executor_ref ex) const
375   { 346   {
HITCBC 376   30 return s_.get().wait(h, ex, w_, token_, &ec_); 347   30 return s_.get().wait(h, ex, w_, token_, &ec_);
377   } 348   }
378   }; 349   };
379   350  
380   /// Represent the awaitable returned by @ref send. 351   /// Represent the awaitable returned by @ref send.
381   struct send_awaitable 352   struct send_awaitable
382   : detail::bytes_op_base<send_awaitable> 353   : detail::bytes_op_base<send_awaitable>
383   { 354   {
384   udp_socket& s_; 355   udp_socket& s_;
385   buffer_param buf_; 356   buffer_param buf_;
386   int flags_; 357   int flags_;
387   358  
HITCBC 388   26 send_awaitable( 359   26 send_awaitable(
389   udp_socket& s, buffer_param buf, 360   udp_socket& s, buffer_param buf,
390   int flags = 0) noexcept 361   int flags = 0) noexcept
HITCBC 391   26 : s_(s), buf_(buf), flags_(flags) {} 362   26 : s_(s), buf_(buf), flags_(flags) {}
392   363  
HITCBC 393   24 std::coroutine_handle<> dispatch( 364   24 std::coroutine_handle<> dispatch(
394   std::coroutine_handle<> h, capy::executor_ref ex) const 365   std::coroutine_handle<> h, capy::executor_ref ex) const
395   { 366   {
HITCBC 396   48 return s_.get().send( 367   48 return s_.get().send(
HITCBC 397   48 h, ex, buf_, flags_, token_, &ec_, &bytes_); 368   48 h, ex, buf_, flags_, token_, &ec_, &bytes_);
398   } 369   }
399   }; 370   };
400   371  
401   /// Represent the awaitable returned by @ref recv. 372   /// Represent the awaitable returned by @ref recv.
402   struct recv_awaitable 373   struct recv_awaitable
403   : detail::bytes_op_base<recv_awaitable> 374   : detail::bytes_op_base<recv_awaitable>
404   { 375   {
405   udp_socket& s_; 376   udp_socket& s_;
406   buffer_param buf_; 377   buffer_param buf_;
407   int flags_; 378   int flags_;
408   379  
HITCBC 409   61 recv_awaitable( 380   61 recv_awaitable(
410   udp_socket& s, buffer_param buf, 381   udp_socket& s, buffer_param buf,
411   int flags = 0) noexcept 382   int flags = 0) noexcept
HITCBC 412   61 : s_(s), buf_(buf), flags_(flags) {} 383   61 : s_(s), buf_(buf), flags_(flags) {}
413   384  
HITCBC 414   59 std::coroutine_handle<> dispatch( 385   59 std::coroutine_handle<> dispatch(
415   std::coroutine_handle<> h, capy::executor_ref ex) const 386   std::coroutine_handle<> h, capy::executor_ref ex) const
416   { 387   {
HITCBC 417   118 return s_.get().recv( 388   118 return s_.get().recv(
HITCBC 418   118 h, ex, buf_, flags_, token_, &ec_, &bytes_); 389   118 h, ex, buf_, flags_, token_, &ec_, &bytes_);
419   } 390   }
420   }; 391   };
421   392  
422   public: 393   public:
423   /** Destructor. 394   /** Destructor.
424   395  
425   Closes the socket if open, cancelling any pending operations. 396   Closes the socket if open, cancelling any pending operations.
426   */ 397   */
427   ~udp_socket() override; 398   ~udp_socket() override;
428   399  
429   /** Construct a socket from an execution context. 400   /** Construct a socket from an execution context.
430   401  
431   @param ctx The execution context that will own this socket. 402   @param ctx The execution context that will own this socket.
432   */ 403   */
433   explicit udp_socket(capy::execution_context& ctx); 404   explicit udp_socket(capy::execution_context& ctx);
434   405  
435   /** Construct a socket from an executor. 406   /** Construct a socket from an executor.
436   407  
437   The socket is associated with the executor's context. 408   The socket is associated with the executor's context.
438   409  
439   @param ex The executor whose context will own the socket. 410   @param ex The executor whose context will own the socket.
440   */ 411   */
441   template<class Ex> 412   template<class Ex>
442   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) && 413   requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
443   capy::Executor<Ex> 414   capy::Executor<Ex>
444   explicit udp_socket(Ex const& ex) : udp_socket(ex.context()) 415   explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
445   { 416   {
446   } 417   }
447   418  
448   /** Move constructor. 419   /** Move constructor.
449   420  
450   Transfers ownership of the socket resources. 421   Transfers ownership of the socket resources.
451   422  
452   @param other The socket to move from. 423   @param other The socket to move from.
453   */ 424   */
HITCBC 454   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {} 425   4 udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
455   426  
456   /** Move assignment operator. 427   /** Move assignment operator.
457   428  
458   Closes any existing socket and transfers ownership. 429   Closes any existing socket and transfers ownership.
459   430  
460   @param other The socket to move from. 431   @param other The socket to move from.
461   @return Reference to this socket. 432   @return Reference to this socket.
462   */ 433   */
HITCBC 463   2 udp_socket& operator=(udp_socket&& other) noexcept 434   2 udp_socket& operator=(udp_socket&& other) noexcept
464   { 435   {
HITCBC 465   2 if (this != &other) 436   2 if (this != &other)
466   { 437   {
HITCBC 467   2 close(); 438   2 close();
HITCBC 468   2 h_ = std::move(other.h_); 439   2 h_ = std::move(other.h_);
469   } 440   }
HITCBC 470   2 return *this; 441   2 return *this;
471   } 442   }
472   443  
473   udp_socket(udp_socket const&) = delete; 444   udp_socket(udp_socket const&) = delete;
474   udp_socket& operator=(udp_socket const&) = delete; 445   udp_socket& operator=(udp_socket const&) = delete;
475   446  
476   /** Open the socket. 447   /** Open the socket.
477   448  
478   Creates a UDP socket and associates it with the platform 449   Creates a UDP socket and associates it with the platform
479   reactor. 450   reactor.
480   451  
481   Failures such as descriptor exhaustion are normal runtime 452   Failures such as descriptor exhaustion are normal runtime
482   conditions and are reported through the returned error code. 453   conditions and are reported through the returned error code.
483   Opening an already-open socket is a no-op that reports 454   Opening an already-open socket is a no-op that reports
484   success. 455   success.
485   456  
486   @param proto The protocol (IPv4 or IPv6). Defaults to 457   @param proto The protocol (IPv4 or IPv6). Defaults to
487   `udp::v4()`. 458   `udp::v4()`.
488   459  
489   @return The error code, empty on success. 460   @return The error code, empty on success.
490   */ 461   */
491   [[nodiscard]] std::error_code open(udp proto = udp::v4()) noexcept; 462   [[nodiscard]] std::error_code open(udp proto = udp::v4()) noexcept;
492   463  
493   /** Close the socket. 464   /** Close the socket.
494   465  
495   Releases socket resources. Any pending operations complete 466   Releases socket resources. Any pending operations complete
496   with `errc::operation_canceled`. 467   with `errc::operation_canceled`.
497   */ 468   */
498   void close() noexcept; 469   void close() noexcept;
499   470  
500   /** Check if the socket is open. 471   /** Check if the socket is open.
501   472  
502   @return `true` if the socket is open and ready for operations. 473   @return `true` if the socket is open and ready for operations.
503   */ 474   */
HITCBC 504   1712 bool is_open() const noexcept 475   1712 bool is_open() const noexcept
505   { 476   {
506   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 477   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
507   return h_ && get().native_handle() != ~native_handle_type(0); 478   return h_ && get().native_handle() != ~native_handle_type(0);
508   #else 479   #else
HITCBC 509   1712 return h_ && get().native_handle() >= 0; 480   1712 return h_ && get().native_handle() >= 0;
510   #endif 481   #endif
511   } 482   }
512   483  
513   /** Bind the socket to a local endpoint. 484   /** Bind the socket to a local endpoint.
514   485  
515   Associates the socket with a local address and port. 486   Associates the socket with a local address and port.
516   Required before calling `recv_from`. 487   Required before calling `recv_from`.
517   488  
518   @param ep The local endpoint to bind to. 489   @param ep The local endpoint to bind to.
519   490  
520   @return Error code on failure, empty on success. 491   @return Error code on failure, empty on success.
521   492  
522   A closed socket reports `errc::bad_file_descriptor`. 493   A closed socket reports `errc::bad_file_descriptor`.
523   */ 494   */
524   [[nodiscard]] std::error_code bind(endpoint ep) noexcept; 495   [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
525   496  
526   /** Disable sends or receives on the socket. 497   /** Disable sends or receives on the socket.
527   498  
528   Failures such as an unconnected socket are normal runtime 499   Failures such as an unconnected socket are normal runtime
529   conditions and are reported through the returned error 500   conditions and are reported through the returned error
530   code. A closed socket reports `errc::bad_file_descriptor`. 501   code. A closed socket reports `errc::bad_file_descriptor`.
531   502  
532   @param what Determines what operations will no longer be 503   @param what Determines what operations will no longer be
533   allowed. 504   allowed.
534   505  
535   @return The error code, empty on success. 506   @return The error code, empty on success.
536   */ 507   */
537   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept; 508   [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
538   509  
539   /** Cancel any pending asynchronous operations. 510   /** Cancel any pending asynchronous operations.
540   511  
541   All outstanding operations complete with 512   All outstanding operations complete with
542   `errc::operation_canceled`. Check `ec == cond::canceled` 513   `errc::operation_canceled`. Check `ec == cond::canceled`
543   for portable comparison. 514   for portable comparison.
544   */ 515   */
545   void cancel() noexcept; 516   void cancel() noexcept;
546   517  
547   /** Get the native socket handle. 518   /** Get the native socket handle.
548   519  
549   @return The native socket handle, or -1 if not open. 520   @return The native socket handle, or -1 if not open.
550   */ 521   */
551   native_handle_type native_handle() const noexcept; 522   native_handle_type native_handle() const noexcept;
552   523  
553   /** Assign an existing native socket to this object. 524   /** Assign an existing native socket to this object.
554   525  
555   Adopts a UDP socket created outside the library — received 526   Adopts a UDP socket created outside the library — received
556   from another process, inherited, or made natively — and 527   from another process, inherited, or made natively — and
557   registers it with the backend. The socket must be a datagram 528   registers it with the backend. The socket must be a datagram
558   socket in the `AF_INET` or `AF_INET6` family. Adoption never 529   socket in the `AF_INET` or `AF_INET6` family. Adoption never
559   alters the descriptor's flags or options: on POSIX the fd 530   alters the descriptor's flags or options: on POSIX the fd
560   must already be non-blocking, and on Windows the socket must 531   must already be non-blocking, and on Windows the socket must
561   be overlapped-capable. 532   be overlapped-capable.
562   533  
563   If this object is already open, pending operations complete 534   If this object is already open, pending operations complete
564   with `errc::operation_canceled` and the held socket is 535   with `errc::operation_canceled` and the held socket is
565   closed before the new one is adopted. 536   closed before the new one is adopted.
566   537  
567   @par Exception Safety 538   @par Exception Safety
568   Strong guarantee on validation failure: the object is 539   Strong guarantee on validation failure: the object is
569   unchanged. If backend registration fails, the object either 540   unchanged. If backend registration fails, the object either
570   retains its previous socket or is left closed, depending on 541   retains its previous socket or is left closed, depending on
571   the backend. In all failure cases the caller retains 542   the backend. In all failure cases the caller retains
572   ownership of `fd`. 543   ownership of `fd`.
573   544  
574   @param fd The native socket to adopt. On success the object 545   @param fd The native socket to adopt. On success the object
575   owns it and will close it. 546   owns it and will close it.
576   547  
577   @return The error code, empty on success. Validation and 548   @return The error code, empty on success. Validation and
578   registration failures are normal runtime conditions when 549   registration failures are normal runtime conditions when
579   adopting foreign descriptors. 550   adopting foreign descriptors.
580   */ 551   */
581   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept; 552   [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
582   553  
583   /** Release ownership of the native socket handle. 554   /** Release ownership of the native socket handle.
584   555  
585   Deregisters the socket from the backend and cancels pending 556   Deregisters the socket from the backend and cancels pending
586   operations without closing the descriptor. The caller takes 557   operations without closing the descriptor. The caller takes
587   ownership of the returned handle. 558   ownership of the returned handle.
588   559  
589   @return The native handle. 560   @return The native handle.
590   561  
591   @throws std::system_error `errc::bad_file_descriptor` if the 562   @throws std::system_error `errc::bad_file_descriptor` if the
592   socket is not open. 563   socket is not open.
593   564  
594   @post is_open() == false 565   @post is_open() == false
595   */ 566   */
596   native_handle_type release(); 567   native_handle_type release();
597   568  
598   /** Set a socket option. 569   /** Set a socket option.
599   570  
600   @param opt The option to set. 571   @param opt The option to set.
601   572  
602   @throws std::system_error `errc::bad_file_descriptor` if the 573   @throws std::system_error `errc::bad_file_descriptor` if the
603   socket is not open; otherwise thrown on failure. 574   socket is not open; otherwise thrown on failure.
604   */ 575   */
605   template<class Option> 576   template<class Option>
HITCBC 606   91 void set_option(Option const& opt) 577   91 void set_option(Option const& opt)
607   { 578   {
HITCBC 608   91 if (!is_open()) 579   91 if (!is_open())
HITCBC 609   2 detail::throw_system_error( 580   2 detail::throw_system_error(
HITCBC 610   4 make_error_code(std::errc::bad_file_descriptor), 581   4 make_error_code(std::errc::bad_file_descriptor),
611   "udp_socket::set_option"); 582   "udp_socket::set_option");
HITCBC 612   89 std::error_code ec = get().set_option( 583   89 std::error_code ec = get().set_option(
613   Option::level(), Option::name(), opt.data(), opt.size()); 584   Option::level(), Option::name(), opt.data(), opt.size());
HITCBC 614   89 if (ec) 585   89 if (ec)
HITCBC 615   6 detail::throw_system_error(ec, "udp_socket::set_option"); 586   6 detail::throw_system_error(ec, "udp_socket::set_option");
HITCBC 616   83 } 587   83 }
617   588  
618   /** Get a socket option. 589   /** Get a socket option.
619   590  
620   @return The current option value. 591   @return The current option value.
621   592  
622   @throws std::system_error `errc::bad_file_descriptor` if the 593   @throws std::system_error `errc::bad_file_descriptor` if the
623   socket is not open; otherwise thrown on failure. 594   socket is not open; otherwise thrown on failure.
624   */ 595   */
625   template<class Option> 596   template<class Option>
HITCBC 626   57 Option get_option() const 597   57 Option get_option() const
627   { 598   {
HITCBC 628   57 if (!is_open()) 599   57 if (!is_open())
HITCBC 629   2 detail::throw_system_error( 600   2 detail::throw_system_error(
HITCBC 630   4 make_error_code(std::errc::bad_file_descriptor), 601   4 make_error_code(std::errc::bad_file_descriptor),
631   "udp_socket::get_option"); 602   "udp_socket::get_option");
HITCBC 632   55 Option opt{}; 603   55 Option opt{};
HITCBC 633   55 std::size_t sz = opt.size(); 604   55 std::size_t sz = opt.size();
634   std::error_code ec = 605   std::error_code ec =
HITCBC 635   55 get().get_option(Option::level(), Option::name(), opt.data(), &sz); 606   55 get().get_option(Option::level(), Option::name(), opt.data(), &sz);
HITCBC 636   55 if (ec) 607   55 if (ec)
HITCBC 637   2 detail::throw_system_error(ec, "udp_socket::get_option"); 608   2 detail::throw_system_error(ec, "udp_socket::get_option");
HITCBC 638   53 opt.resize(sz); 609   53 opt.resize(sz);
HITCBC 639   53 return opt; 610   53 return opt;
640   } 611   }
641   612  
642   /** Get the local endpoint of the socket. 613   /** Get the local endpoint of the socket.
643   614  
644   @return The local endpoint, or a default endpoint if not bound. 615   @return The local endpoint, or a default endpoint if not bound.
645   */ 616   */
646   endpoint local_endpoint() const noexcept; 617   endpoint local_endpoint() const noexcept;
647   618  
648   /** Send a datagram to the specified destination. 619   /** Send a datagram to the specified destination.
649   620  
650   @param buf The buffer containing data to send. 621   @param buf The buffer containing data to send.
651   @param dest The destination endpoint. 622   @param dest The destination endpoint.
652   @param flags Message flags (e.g. message_flags::dont_route). 623   @param flags Message flags (e.g. message_flags::dont_route).
653   624  
654   @return An awaitable that completes with 625   @return An awaitable that completes with
655   `io_result<std::size_t>`. 626   `io_result<std::size_t>`.
656   627  
657   A closed socket reports `errc::bad_file_descriptor`. 628   A closed socket reports `errc::bad_file_descriptor`.
658   */ 629   */
659   template<capy::ConstBufferSequence Buffers> 630   template<capy::ConstBufferSequence Buffers>
HITCBC 660   71 [[nodiscard]] auto send_to( 631   71 [[nodiscard]] auto send_to(
661   Buffers const& buf, 632   Buffers const& buf,
662   endpoint dest, 633   endpoint dest,
663   corosio::message_flags flags) 634   corosio::message_flags flags)
664   { 635   {
HITCBC 665   71 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags)); 636   71 send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
HITCBC 666   71 if (!is_open()) 637   71 if (!is_open())
HITCBC 667   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 638   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 668   71 return aw; 639   71 return aw;
669   } 640   }
670   641  
671   /// @overload 642   /// @overload
672   template<capy::ConstBufferSequence Buffers> 643   template<capy::ConstBufferSequence Buffers>
HITCBC 673   71 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest) 644   71 [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
674   { 645   {
HITCBC 675   71 return send_to(buf, dest, corosio::message_flags::none); 646   71 return send_to(buf, dest, corosio::message_flags::none);
676   } 647   }
677   648  
678   /** Receive a datagram and capture the sender's endpoint. 649   /** Receive a datagram and capture the sender's endpoint.
679   650  
680   @param buf The buffer to receive data into. 651   @param buf The buffer to receive data into.
681   @param source Reference to an endpoint that will be set to 652   @param source Reference to an endpoint that will be set to
682   the sender's address on successful completion. 653   the sender's address on successful completion.
683   @param flags Message flags (e.g. message_flags::peek). 654   @param flags Message flags (e.g. message_flags::peek).
684   655  
685   @return An awaitable that completes with 656   @return An awaitable that completes with
686   `io_result<std::size_t>`. 657   `io_result<std::size_t>`.
687   658  
688   A closed socket reports `errc::bad_file_descriptor`. 659   A closed socket reports `errc::bad_file_descriptor`.
689   */ 660   */
690   template<capy::MutableBufferSequence Buffers> 661   template<capy::MutableBufferSequence Buffers>
HITCBC 691   91 [[nodiscard]] auto recv_from( 662   91 [[nodiscard]] auto recv_from(
692   Buffers const& buf, 663   Buffers const& buf,
693   endpoint& source, 664   endpoint& source,
694   corosio::message_flags flags) 665   corosio::message_flags flags)
695   { 666   {
HITCBC 696   91 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags)); 667   91 recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
HITCBC 697   91 if (!is_open()) 668   91 if (!is_open())
HITCBC 698   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 669   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 699   91 return aw; 670   91 return aw;
700   } 671   }
701   672  
702   /// @overload 673   /// @overload
703   template<capy::MutableBufferSequence Buffers> 674   template<capy::MutableBufferSequence Buffers>
HITCBC 704   90 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source) 675   90 [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
705   { 676   {
HITCBC 706   90 return recv_from(buf, source, corosio::message_flags::none); 677   90 return recv_from(buf, source, corosio::message_flags::none);
707   } 678   }
708   679  
709   /** Initiate an asynchronous connect to set the default peer. 680   /** Initiate an asynchronous connect to set the default peer.
710   681  
711   If the socket is not already open, it is opened automatically 682   If the socket is not already open, it is opened automatically
712   using the address family of @p ep. 683   using the address family of @p ep.
713   684  
714   @param ep The remote endpoint to connect to. 685   @param ep The remote endpoint to connect to.
715   686  
716   @return An awaitable that completes with `io_result<>`. 687   @return An awaitable that completes with `io_result<>`.
717   688  
718   If the socket needs to be opened and the open fails, the 689   If the socket needs to be opened and the open fails, the
719   awaitable completes immediately with that error. 690   awaitable completes immediately with that error.
720   */ 691   */
HITCBC 721   40 [[nodiscard]] auto connect(endpoint ep) 692   40 [[nodiscard]] auto connect(endpoint ep)
722   { 693   {
HITCBC 723   40 connect_awaitable aw(*this, ep); 694   40 connect_awaitable aw(*this, ep);
HITCBC 724   40 if (!is_open()) 695   40 if (!is_open())
HITCBC 725   8 aw.ec_ = open(ep.is_v6() ? udp::v6() : udp::v4()); 696   8 aw.ec_ = open(ep.is_v6() ? udp::v6() : udp::v4());
HITCBC 726   40 return aw; 697   40 return aw;
727   } 698   }
728   699  
729   /** Wait for the socket to become ready in a given direction. 700   /** Wait for the socket to become ready in a given direction.
730   701  
731   Suspends until the socket is ready for the requested 702   Suspends until the socket is ready for the requested
732   direction, or an error condition is reported. No bytes 703   direction, or an error condition is reported. No bytes
733   are transferred. 704   are transferred.
734   705  
735   The operation supports cancellation via `std::stop_token`. 706   The operation supports cancellation via `std::stop_token`.
736   707  
737   @param w The wait direction (read, write, or error). 708   @param w The wait direction (read, write, or error).
738   709  
739   @return An awaitable that completes with `io_result<>`. 710   @return An awaitable that completes with `io_result<>`.
740   711  
741   A closed socket completes with `errc::bad_file_descriptor`. 712   A closed socket completes with `errc::bad_file_descriptor`.
742   713  
743   @par Preconditions 714   @par Preconditions
744   This socket must outlive the returned awaitable. 715   This socket must outlive the returned awaitable.
745   */ 716   */
HITCBC 746   30 [[nodiscard]] auto wait(wait_type w) 717   30 [[nodiscard]] auto wait(wait_type w)
747   { 718   {
HITCBC 748   30 return wait_awaitable(*this, w); 719   30 return wait_awaitable(*this, w);
749   } 720   }
750   721  
751   /** Send a datagram to the connected peer. 722   /** Send a datagram to the connected peer.
752   723  
753   @param buf The buffer containing data to send. 724   @param buf The buffer containing data to send.
754   @param flags Message flags. 725   @param flags Message flags.
755   726  
756   @return An awaitable that completes with 727   @return An awaitable that completes with
757   `io_result<std::size_t>`. 728   `io_result<std::size_t>`.
758   729  
759   A closed socket reports `errc::bad_file_descriptor`. 730   A closed socket reports `errc::bad_file_descriptor`.
760   */ 731   */
761   template<capy::ConstBufferSequence Buffers> 732   template<capy::ConstBufferSequence Buffers>
HITCBC 762   26 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags) 733   26 [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
763   { 734   {
HITCBC 764   26 send_awaitable aw(*this, buf, static_cast<int>(flags)); 735   26 send_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 765   26 if (!is_open()) 736   26 if (!is_open())
HITCBC 766   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 737   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 767   26 return aw; 738   26 return aw;
768   } 739   }
769   740  
770   /// @overload 741   /// @overload
771   template<capy::ConstBufferSequence Buffers> 742   template<capy::ConstBufferSequence Buffers>
HITCBC 772   26 [[nodiscard]] auto send(Buffers const& buf) 743   26 [[nodiscard]] auto send(Buffers const& buf)
773   { 744   {
HITCBC 774   26 return send(buf, corosio::message_flags::none); 745   26 return send(buf, corosio::message_flags::none);
775   } 746   }
776   747  
777   /** Receive a datagram from the connected peer. 748   /** Receive a datagram from the connected peer.
778   749  
779   @param buf The buffer to receive data into. 750   @param buf The buffer to receive data into.
780   @param flags Message flags (e.g. message_flags::peek). 751   @param flags Message flags (e.g. message_flags::peek).
781   752  
782   @return An awaitable that completes with 753   @return An awaitable that completes with
783   `io_result<std::size_t>`. 754   `io_result<std::size_t>`.
784   755  
785   A closed socket reports `errc::bad_file_descriptor`. 756   A closed socket reports `errc::bad_file_descriptor`.
786   */ 757   */
787   template<capy::MutableBufferSequence Buffers> 758   template<capy::MutableBufferSequence Buffers>
HITCBC 788   61 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags) 759   61 [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
789   { 760   {
HITCBC 790   61 recv_awaitable aw(*this, buf, static_cast<int>(flags)); 761   61 recv_awaitable aw(*this, buf, static_cast<int>(flags));
HITCBC 791   61 if (!is_open()) 762   61 if (!is_open())
HITCBC 792   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 763   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 793   61 return aw; 764   61 return aw;
794   } 765   }
795   766  
796   /// @overload 767   /// @overload
797   template<capy::MutableBufferSequence Buffers> 768   template<capy::MutableBufferSequence Buffers>
HITCBC 798   61 [[nodiscard]] auto recv(Buffers const& buf) 769   61 [[nodiscard]] auto recv(Buffers const& buf)
799   { 770   {
HITCBC 800   61 return recv(buf, corosio::message_flags::none); 771   61 return recv(buf, corosio::message_flags::none);
801   } 772   }
802   773  
803   /** Get the remote endpoint of the socket. 774   /** Get the remote endpoint of the socket.
804   775  
805   Returns the address and port of the connected peer. 776   Returns the address and port of the connected peer.
806   777  
807   @return The remote endpoint, or a default endpoint if 778   @return The remote endpoint, or a default endpoint if
808   not connected. 779   not connected.
809   */ 780   */
810   endpoint remote_endpoint() const noexcept; 781   endpoint remote_endpoint() const noexcept;
811   782  
812   protected: 783   protected:
813   /// Construct from a pre-built handle (for native_udp_socket). 784   /// Construct from a pre-built handle (for native_udp_socket).
HITCBC 814   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h)) 785   42 explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
815   { 786   {
HITCBC 816   42 } 787   42 }
817   788  
818   private: 789   private:
819   /// Open the socket for the given protocol triple. 790   /// Open the socket for the given protocol triple.
820   [[nodiscard]] std::error_code 791   [[nodiscard]] std::error_code
821   open_for_family(int family, int type, int protocol) noexcept; 792   open_for_family(int family, int type, int protocol) noexcept;
822   793  
HITCBC 823   2365 inline implementation& get() const noexcept 794   2365 inline implementation& get() const noexcept
824   { 795   {
HITCBC 825   2365 return *static_cast<implementation*>(h_.get()); 796   2365 return *static_cast<implementation*>(h_.get());
826   } 797   }
827   }; 798   };
828   799  
829   } // namespace boost::corosio 800   } // namespace boost::corosio
830   801  
831   #endif // BOOST_COROSIO_UDP_SOCKET_HPP 802   #endif // BOOST_COROSIO_UDP_SOCKET_HPP