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