include/boost/corosio/udp_socket.hpp

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