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 119x 100.0% 100.0% boost::corosio::udp_socket::send_to_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :290 116x 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 184x 100.0% 100.0% boost::corosio::udp_socket::recv_from_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :322 181x 100.0% 80.0% boost::corosio::udp_socket::connect_awaitable::connect_awaitable(boost::corosio::udp_socket&, boost::corosio::endpoint) :335 96x 100.0% 100.0% boost::corosio::udp_socket::connect_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :342 96x 100.0% 80.0% boost::corosio::udp_socket::wait_awaitable::wait_awaitable(boost::corosio::udp_socket&, boost::corosio::wait_type) :354 39x 100.0% 100.0% boost::corosio::udp_socket::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :357 39x 100.0% 80.0% boost::corosio::udp_socket::send_awaitable::send_awaitable(boost::corosio::udp_socket&, boost::corosio::buffer_param, int) :370 173x 100.0% 100.0% boost::corosio::udp_socket::send_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :378 170x 100.0% 80.0% boost::corosio::udp_socket::recv_awaitable::recv_awaitable(boost::corosio::udp_socket&, boost::corosio::buffer_param, int) :391 127x 100.0% 100.0% boost::corosio::udp_socket::recv_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :399 124x 100.0% 80.0% boost::corosio::udp_socket::udp_socket(boost::corosio::udp_socket&&) :437 6x 100.0% 100.0% boost::corosio::udp_socket::operator=(boost::corosio::udp_socket&&) :446 3x 100.0% 100.0% boost::corosio::udp_socket::is_open() const :487 2695x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 62.5% 75.0% void boost::corosio::udp_socket::set_option<boost::corosio::socket_option::broadcast>(boost::corosio::socket_option::broadcast const&) :589 10x 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 6x 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 3x 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 3x 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 3x 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 6x 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 3x 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 3x 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 3x 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 15x 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 6x 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 6x 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 14x 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 4x 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 3x 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 9x 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 3x 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 3x 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 3x 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 3x 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 3x 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 3x 72.7% 78.0% boost::corosio::socket_option::broadcast boost::corosio::udp_socket::get_option<boost::corosio::socket_option::broadcast>() const :609 10x 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 6x 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 3x 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 3x 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 12x 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 6x 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 12x 72.7% 78.0% boost::corosio::socket_option::reuse_address boost::corosio::udp_socket::get_option<boost::corosio::socket_option::reuse_address>() const :609 3x 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 3x 72.7% 78.0% boost::corosio::socket_option::v6_only boost::corosio::udp_socket::get_option<boost::corosio::socket_option::v6_only>() const :609 9x 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 119x 100.0% 100.0% auto boost::corosio::udp_socket::send_to<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::endpoint) :654 119x 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 184x 100.0% 100.0% auto boost::corosio::udp_socket::recv_from<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::endpoint&) :683 183x 100.0% 100.0% boost::corosio::udp_socket::connect(boost::corosio::endpoint) :700 96x 100.0% 100.0% boost::corosio::udp_socket::wait(boost::corosio::wait_type) :725 39x 100.0% 100.0% auto boost::corosio::udp_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&, boost::corosio::message_flags) :741 173x 100.0% 100.0% auto boost::corosio::udp_socket::send<boost::capy::const_buffer>(boost::capy::const_buffer const&) :751 173x 100.0% 100.0% auto boost::corosio::udp_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&, boost::corosio::message_flags) :767 127x 100.0% 100.0% auto boost::corosio::udp_socket::recv<boost::capy::mutable_buffer>(boost::capy::mutable_buffer const&) :777 127x 100.0% 100.0% boost::corosio::udp_socket::udp_socket(boost::corosio::io_object::handle) :793 63x 100.0% 100.0% boost::corosio::udp_socket::get() const :802 3901x 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 119x send_to_awaitable(
278 udp_socket& s,
279 buffer_param buf,
280 endpoint dest,
281 int flags = 0) noexcept
282 238x : s_(s)
283 119x , buf_(buf)
284 119x , dest_(dest)
285 119x , flags_(flags)
286 {
287 119x }
288
289 std::coroutine_handle<>
290 116x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
291 {
292 232x return s_.get().send_to(
293 232x 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 184x recv_from_awaitable(
310 udp_socket& s,
311 buffer_param buf,
312 endpoint& source,
313 int flags = 0) noexcept
314 368x : s_(s)
315 184x , buf_(buf)
316 184x , source_(source)
317 184x , flags_(flags)
318 {
319 184x }
320
321 std::coroutine_handle<>
322 181x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
323 {
324 362x return s_.get().recv_from(
325 362x 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 96x connect_awaitable(udp_socket& s, endpoint ep) noexcept
336 192x : s_(s)
337 96x , endpoint_(ep)
338 {
339 96x }
340
341 std::coroutine_handle<>
342 96x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
343 {
344 96x 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 39x wait_awaitable(udp_socket& s, wait_type w) noexcept : s_(s), w_(w) {}
355
356 std::coroutine_handle<>
357 39x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
358 {
359 39x 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 173x send_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
371 346x : s_(s)
372 173x , buf_(buf)
373 173x , flags_(flags)
374 {
375 173x }
376
377 std::coroutine_handle<>
378 170x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
379 {
380 170x 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 127x recv_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
392 254x : s_(s)
393 127x , buf_(buf)
394 127x , flags_(flags)
395 {
396 127x }
397
398 std::coroutine_handle<>
399 124x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
400 {
401 124x 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 6x 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 3x udp_socket& operator=(udp_socket&& other) noexcept
447 {
448 3x if (this != &other)
449 {
450 3x close();
451 3x h_ = std::move(other.h_);
452 }
453 3x 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 2695x 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 2695x 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 136x void set_option(Option const& opt)
590 {
591 136x if (!is_open())
592 3x detail::throw_system_error(
593 6x make_error_code(std::errc::bad_file_descriptor),
594 "udp_socket::set_option");
595 133x std::error_code ec = get().set_option(
596 Option::level(), Option::name(), opt.data(), opt.size());
597 133x if (ec)
598 9x detail::throw_system_error(ec, "udp_socket::set_option");
599 124x }
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 85x Option get_option() const
610 {
611 85x if (!is_open())
612 3x detail::throw_system_error(
613 6x make_error_code(std::errc::bad_file_descriptor),
614 "udp_socket::get_option");
615 82x Option opt{};
616 82x std::size_t sz = opt.size();
617 std::error_code ec =
618 82x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
619 82x if (ec)
620 3x detail::throw_system_error(ec, "udp_socket::get_option");
621 79x opt.resize(sz);
622 79x 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 119x send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags)
645 {
646 119x send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
647 119x if (!is_open())
648 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
649 119x return aw;
650 }
651
652 /// @overload
653 template<capy::ConstBufferSequence Buffers>
654 119x [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
655 {
656 119x 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 184x [[nodiscard]] auto recv_from(
673 Buffers const& buf, endpoint& source, corosio::message_flags flags)
674 {
675 184x recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
676 184x if (!is_open())
677 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
678 184x return aw;
679 }
680
681 /// @overload
682 template<capy::MutableBufferSequence Buffers>
683 183x [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
684 {
685 183x 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 96x [[nodiscard]] auto connect(endpoint ep)
701 {
702 96x connect_awaitable aw(*this, ep);
703 96x if (!is_open())
704 12x aw.ec_ = open(ep.is_v6() ? udp::v6() : udp::v4());
705 96x 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 39x [[nodiscard]] auto wait(wait_type w)
726 {
727 39x 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 173x [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
742 {
743 173x send_awaitable aw(*this, buf, static_cast<int>(flags));
744 173x if (!is_open())
745 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
746 173x return aw;
747 }
748
749 /// @overload
750 template<capy::ConstBufferSequence Buffers>
751 173x [[nodiscard]] auto send(Buffers const& buf)
752 {
753 173x 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 127x [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
768 {
769 127x recv_awaitable aw(*this, buf, static_cast<int>(flags));
770 127x if (!is_open())
771 3x aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
772 127x return aw;
773 }
774
775 /// @overload
776 template<capy::MutableBufferSequence Buffers>
777 127x [[nodiscard]] auto recv(Buffers const& buf)
778 {
779 127x 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 63x explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
794 {
795 63x }
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 3901x inline implementation& get() const noexcept
803 {
804 3901x return *static_cast<implementation*>(h_.get());
805 }
806 };
807
808 } // namespace boost::corosio
809
810 #endif // BOOST_COROSIO_UDP_SOCKET_HPP
811