include/boost/corosio/udp_socket.hpp

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