include/boost/corosio/udp_socket.hpp

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