include/boost/corosio/udp_socket.hpp

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