include/boost/corosio/udp_socket.hpp

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