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