include/boost/corosio/udp_socket.hpp

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