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