include/boost/corosio/udp_socket.hpp

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