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