1  
//
1  
//
2  
// Copyright (c) 2026 Steve Gerbino
2  
// Copyright (c) 2026 Steve Gerbino
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

10  
#ifndef BOOST_COROSIO_UDP_SOCKET_HPP
10  
#ifndef BOOST_COROSIO_UDP_SOCKET_HPP
11  
#define BOOST_COROSIO_UDP_SOCKET_HPP
11  
#define BOOST_COROSIO_UDP_SOCKET_HPP
12  

12  

13  
#include <boost/corosio/detail/config.hpp>
13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/platform.hpp>
14  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/except.hpp>
15  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/detail/native_handle.hpp>
16  
#include <boost/corosio/detail/native_handle.hpp>
17  
#include <boost/corosio/detail/op_base.hpp>
17  
#include <boost/corosio/detail/op_base.hpp>
18  
#include <boost/corosio/io/io_object.hpp>
18  
#include <boost/corosio/io/io_object.hpp>
19  
#include <boost/capy/io_result.hpp>
19  
#include <boost/capy/io_result.hpp>
20  
#include <boost/corosio/detail/buffer_param.hpp>
20  
#include <boost/corosio/detail/buffer_param.hpp>
21  
#include <boost/corosio/endpoint.hpp>
21  
#include <boost/corosio/endpoint.hpp>
22  
#include <boost/corosio/message_flags.hpp>
22  
#include <boost/corosio/message_flags.hpp>
23  
#include <boost/corosio/udp.hpp>
23  
#include <boost/corosio/udp.hpp>
24  
#include <boost/capy/ex/executor_ref.hpp>
24  
#include <boost/capy/ex/executor_ref.hpp>
25  
#include <boost/capy/ex/execution_context.hpp>
25  
#include <boost/capy/ex/execution_context.hpp>
26  
#include <boost/capy/ex/io_env.hpp>
26  
#include <boost/capy/ex/io_env.hpp>
27  
#include <boost/capy/concept/executor.hpp>
27  
#include <boost/capy/concept/executor.hpp>
28  

28  

29  
#include <system_error>
29  
#include <system_error>
30  

30  

31  
#include <concepts>
31  
#include <concepts>
32  
#include <coroutine>
32  
#include <coroutine>
33  
#include <cstddef>
33  
#include <cstddef>
34  
#include <stop_token>
34  
#include <stop_token>
35  
#include <type_traits>
35  
#include <type_traits>
36  

36  

37  
namespace boost::corosio {
37  
namespace boost::corosio {
38  

38  

39  
/** An asynchronous UDP socket for coroutine I/O.
39  
/** An asynchronous UDP socket for coroutine I/O.
40  

40  

41  
    This class provides asynchronous UDP datagram operations that
41  
    This class provides asynchronous UDP datagram operations that
42  
    return awaitable types. Each operation participates in the affine
42  
    return awaitable types. Each operation participates in the affine
43  
    awaitable protocol, ensuring coroutines resume on the correct
43  
    awaitable protocol, ensuring coroutines resume on the correct
44  
    executor.
44  
    executor.
45  

45  

46  
    Supports two modes of operation:
46  
    Supports two modes of operation:
47  

47  

48  
    **Connectionless mode**: each `send_to` specifies a destination
48  
    **Connectionless mode**: each `send_to` specifies a destination
49  
    endpoint, and each `recv_from` captures the source endpoint.
49  
    endpoint, and each `recv_from` captures the source endpoint.
50  
    The socket must be opened (and optionally bound) before I/O.
50  
    The socket must be opened (and optionally bound) before I/O.
51  

51  

52  
    **Connected mode**: call `connect()` to set a default peer,
52  
    **Connected mode**: call `connect()` to set a default peer,
53  
    then use `send()`/`recv()` without endpoint arguments.
53  
    then use `send()`/`recv()` without endpoint arguments.
54  
    The kernel filters incoming datagrams to those from the
54  
    The kernel filters incoming datagrams to those from the
55  
    connected peer.
55  
    connected peer.
56  

56  

57  
    @par Thread Safety
57  
    @par Thread Safety
58  
    Distinct objects: Safe.@n
58  
    Distinct objects: Safe.@n
59  
    Shared objects: Unsafe. A socket must not have concurrent
59  
    Shared objects: Unsafe. A socket must not have concurrent
60  
    operations of the same type (e.g., two simultaneous recv_from).
60  
    operations of the same type (e.g., two simultaneous recv_from).
61  
    One send_to and one recv_from may be in flight simultaneously.
61  
    One send_to and one recv_from may be in flight simultaneously.
62  

62  

63  
    @par Example
63  
    @par Example
64  
    @code
64  
    @code
65  
    // Connectionless mode
65  
    // Connectionless mode
66  
    io_context ioc;
66  
    io_context ioc;
67  
    udp_socket sock( ioc );
67  
    udp_socket sock( ioc );
68  
    sock.open( udp::v4() );
68  
    sock.open( udp::v4() );
69  
    sock.bind( endpoint( ipv4_address::any(), 9000 ) );
69  
    sock.bind( endpoint( ipv4_address::any(), 9000 ) );
70  

70  

71  
    char buf[1024];
71  
    char buf[1024];
72  
    endpoint sender;
72  
    endpoint sender;
73  
    auto [ec, n] = co_await sock.recv_from(
73  
    auto [ec, n] = co_await sock.recv_from(
74  
        capy::mutable_buffer( buf, sizeof( buf ) ), sender );
74  
        capy::mutable_buffer( buf, sizeof( buf ) ), sender );
75  
    if ( !ec )
75  
    if ( !ec )
76  
        co_await sock.send_to(
76  
        co_await sock.send_to(
77  
            capy::const_buffer( buf, n ), sender );
77  
            capy::const_buffer( buf, n ), sender );
78  

78  

79  
    // Connected mode
79  
    // Connected mode
80  
    udp_socket csock( ioc );
80  
    udp_socket csock( ioc );
81  
    auto [cec] = co_await csock.connect(
81  
    auto [cec] = co_await csock.connect(
82  
        endpoint( ipv4_address::loopback(), 9000 ) );
82  
        endpoint( ipv4_address::loopback(), 9000 ) );
83  
    if ( !cec )
83  
    if ( !cec )
84  
        co_await csock.send(
84  
        co_await csock.send(
85  
            capy::const_buffer( buf, n ) );
85  
            capy::const_buffer( buf, n ) );
86  
    @endcode
86  
    @endcode
87  
*/
87  
*/
88  
class BOOST_COROSIO_DECL udp_socket : public io_object
88  
class BOOST_COROSIO_DECL udp_socket : public io_object
89  
{
89  
{
90  
public:
90  
public:
91  
    /** Define backend hooks for UDP socket operations.
91  
    /** Define backend hooks for UDP socket operations.
92  

92  

93  
        Platform backends (epoll, kqueue, select) derive from
93  
        Platform backends (epoll, kqueue, select) derive from
94  
        this to implement datagram I/O and option management.
94  
        this to implement datagram I/O and option management.
95  
    */
95  
    */
96  
    struct implementation : io_object::implementation
96  
    struct implementation : io_object::implementation
97  
    {
97  
    {
98  
        /** Initiate an asynchronous send_to operation.
98  
        /** Initiate an asynchronous send_to operation.
99  

99  

100  
            @param h Coroutine handle to resume on completion.
100  
            @param h Coroutine handle to resume on completion.
101  
            @param ex Executor for dispatching the completion.
101  
            @param ex Executor for dispatching the completion.
102  
            @param buf The buffer data to send.
102  
            @param buf The buffer data to send.
103  
            @param dest The destination endpoint.
103  
            @param dest The destination endpoint.
104  
            @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
104  
            @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
105  
            @param token Stop token for cancellation.
105  
            @param token Stop token for cancellation.
106  
            @param ec Output error code.
106  
            @param ec Output error code.
107  
            @param bytes_out Output bytes transferred.
107  
            @param bytes_out Output bytes transferred.
108  

108  

109  
            @return Coroutine handle to resume immediately.
109  
            @return Coroutine handle to resume immediately.
110  
        */
110  
        */
111  
        virtual std::coroutine_handle<> send_to(
111  
        virtual std::coroutine_handle<> send_to(
112  
            std::coroutine_handle<> h,
112  
            std::coroutine_handle<> h,
113  
            capy::executor_ref ex,
113  
            capy::executor_ref ex,
114  
            buffer_param buf,
114  
            buffer_param buf,
115  
            endpoint dest,
115  
            endpoint dest,
116  
            int flags,
116  
            int flags,
117  
            std::stop_token token,
117  
            std::stop_token token,
118  
            std::error_code* ec,
118  
            std::error_code* ec,
119  
            std::size_t* bytes_out) = 0;
119  
            std::size_t* bytes_out) = 0;
120  

120  

121  
        /** Initiate an asynchronous recv_from operation.
121  
        /** Initiate an asynchronous recv_from operation.
122  

122  

123  
            @param h Coroutine handle to resume on completion.
123  
            @param h Coroutine handle to resume on completion.
124  
            @param ex Executor for dispatching the completion.
124  
            @param ex Executor for dispatching the completion.
125  
            @param buf The buffer to receive into.
125  
            @param buf The buffer to receive into.
126  
            @param source Output endpoint for the sender's address.
126  
            @param source Output endpoint for the sender's address.
127  
            @param flags Platform message flags (e.g. `MSG_PEEK`).
127  
            @param flags Platform message flags (e.g. `MSG_PEEK`).
128  
            @param token Stop token for cancellation.
128  
            @param token Stop token for cancellation.
129  
            @param ec Output error code.
129  
            @param ec Output error code.
130  
            @param bytes_out Output bytes transferred.
130  
            @param bytes_out Output bytes transferred.
131  

131  

132  
            @return Coroutine handle to resume immediately.
132  
            @return Coroutine handle to resume immediately.
133  
        */
133  
        */
134  
        virtual std::coroutine_handle<> recv_from(
134  
        virtual std::coroutine_handle<> recv_from(
135  
            std::coroutine_handle<> h,
135  
            std::coroutine_handle<> h,
136  
            capy::executor_ref ex,
136  
            capy::executor_ref ex,
137  
            buffer_param buf,
137  
            buffer_param buf,
138  
            endpoint* source,
138  
            endpoint* source,
139  
            int flags,
139  
            int flags,
140  
            std::stop_token token,
140  
            std::stop_token token,
141  
            std::error_code* ec,
141  
            std::error_code* ec,
142  
            std::size_t* bytes_out) = 0;
142  
            std::size_t* bytes_out) = 0;
143  

143  

144  
        /// Return the platform socket descriptor.
144  
        /// Return the platform socket descriptor.
145  
        virtual native_handle_type native_handle() const noexcept = 0;
145  
        virtual native_handle_type native_handle() const noexcept = 0;
146  

146  

147  
        /** Request cancellation of pending asynchronous operations.
147  
        /** Request cancellation of pending asynchronous operations.
148  

148  

149  
            All outstanding operations complete with operation_canceled
149  
            All outstanding operations complete with operation_canceled
150  
            error. Check `ec == cond::canceled` for portable comparison.
150  
            error. Check `ec == cond::canceled` for portable comparison.
151  
        */
151  
        */
152  
        virtual void cancel() noexcept = 0;
152  
        virtual void cancel() noexcept = 0;
153  

153  

154  
        /** Set a socket option.
154  
        /** Set a socket option.
155  

155  

156  
            @param level The protocol level (e.g. `SOL_SOCKET`).
156  
            @param level The protocol level (e.g. `SOL_SOCKET`).
157  
            @param optname The option name.
157  
            @param optname The option name.
158  
            @param data Pointer to the option value.
158  
            @param data Pointer to the option value.
159  
            @param size Size of the option value in bytes.
159  
            @param size Size of the option value in bytes.
160  
            @return Error code on failure, empty on success.
160  
            @return Error code on failure, empty on success.
161  
        */
161  
        */
162  
        virtual std::error_code set_option(
162  
        virtual std::error_code set_option(
163  
            int level,
163  
            int level,
164  
            int optname,
164  
            int optname,
165  
            void const* data,
165  
            void const* data,
166  
            std::size_t size) noexcept = 0;
166  
            std::size_t size) noexcept = 0;
167  

167  

168  
        /** Get a socket option.
168  
        /** Get a socket option.
169  

169  

170  
            @param level The protocol level (e.g. `SOL_SOCKET`).
170  
            @param level The protocol level (e.g. `SOL_SOCKET`).
171  
            @param optname The option name.
171  
            @param optname The option name.
172  
            @param data Pointer to receive the option value.
172  
            @param data Pointer to receive the option value.
173  
            @param size On entry, the size of the buffer. On exit,
173  
            @param size On entry, the size of the buffer. On exit,
174  
                the size of the option value.
174  
                the size of the option value.
175  
            @return Error code on failure, empty on success.
175  
            @return Error code on failure, empty on success.
176  
        */
176  
        */
177  
        virtual std::error_code
177  
        virtual std::error_code
178  
        get_option(int level, int optname, void* data, std::size_t* size)
178  
        get_option(int level, int optname, void* data, std::size_t* size)
179  
            const noexcept = 0;
179  
            const noexcept = 0;
180  

180  

181  
        /// Return the cached local endpoint.
181  
        /// Return the cached local endpoint.
182  
        virtual endpoint local_endpoint() const noexcept = 0;
182  
        virtual endpoint local_endpoint() const noexcept = 0;
183  

183  

184  
        /// Return the cached remote endpoint (connected mode).
184  
        /// Return the cached remote endpoint (connected mode).
185  
        virtual endpoint remote_endpoint() const noexcept = 0;
185  
        virtual endpoint remote_endpoint() const noexcept = 0;
186  

186  

187  
        /** Initiate an asynchronous connect to set the default peer.
187  
        /** Initiate an asynchronous connect to set the default peer.
188  

188  

189  
            @param h Coroutine handle to resume on completion.
189  
            @param h Coroutine handle to resume on completion.
190  
            @param ex Executor for dispatching the completion.
190  
            @param ex Executor for dispatching the completion.
191  
            @param ep The remote endpoint to connect to.
191  
            @param ep The remote endpoint to connect to.
192  
            @param token Stop token for cancellation.
192  
            @param token Stop token for cancellation.
193  
            @param ec Output error code.
193  
            @param ec Output error code.
194  

194  

195  
            @return Coroutine handle to resume immediately.
195  
            @return Coroutine handle to resume immediately.
196  
        */
196  
        */
197  
        virtual std::coroutine_handle<> connect(
197  
        virtual std::coroutine_handle<> connect(
198  
            std::coroutine_handle<> h,
198  
            std::coroutine_handle<> h,
199  
            capy::executor_ref ex,
199  
            capy::executor_ref ex,
200  
            endpoint ep,
200  
            endpoint ep,
201  
            std::stop_token token,
201  
            std::stop_token token,
202  
            std::error_code* ec) = 0;
202  
            std::error_code* ec) = 0;
203  

203  

204  
        /** Initiate an asynchronous connected send operation.
204  
        /** Initiate an asynchronous connected send operation.
205  

205  

206  
            @param h Coroutine handle to resume on completion.
206  
            @param h Coroutine handle to resume on completion.
207  
            @param ex Executor for dispatching the completion.
207  
            @param ex Executor for dispatching the completion.
208  
            @param buf The buffer data to send.
208  
            @param buf The buffer data to send.
209  
            @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
209  
            @param flags Platform message flags (e.g. `MSG_DONTWAIT`).
210  
            @param token Stop token for cancellation.
210  
            @param token Stop token for cancellation.
211  
            @param ec Output error code.
211  
            @param ec Output error code.
212  
            @param bytes_out Output bytes transferred.
212  
            @param bytes_out Output bytes transferred.
213  

213  

214  
            @return Coroutine handle to resume immediately.
214  
            @return Coroutine handle to resume immediately.
215  
        */
215  
        */
216  
        virtual std::coroutine_handle<> send(
216  
        virtual std::coroutine_handle<> send(
217  
            std::coroutine_handle<> h,
217  
            std::coroutine_handle<> h,
218  
            capy::executor_ref ex,
218  
            capy::executor_ref ex,
219  
            buffer_param buf,
219  
            buffer_param buf,
220  
            int flags,
220  
            int flags,
221  
            std::stop_token token,
221  
            std::stop_token token,
222  
            std::error_code* ec,
222  
            std::error_code* ec,
223  
            std::size_t* bytes_out) = 0;
223  
            std::size_t* bytes_out) = 0;
224  

224  

225  
        /** Initiate an asynchronous connected recv operation.
225  
        /** Initiate an asynchronous connected recv operation.
226  

226  

227  
            @param h Coroutine handle to resume on completion.
227  
            @param h Coroutine handle to resume on completion.
228  
            @param ex Executor for dispatching the completion.
228  
            @param ex Executor for dispatching the completion.
229  
            @param buf The buffer to receive into.
229  
            @param buf The buffer to receive into.
230  
            @param flags Platform message flags (e.g. `MSG_PEEK`).
230  
            @param flags Platform message flags (e.g. `MSG_PEEK`).
231  
            @param token Stop token for cancellation.
231  
            @param token Stop token for cancellation.
232  
            @param ec Output error code.
232  
            @param ec Output error code.
233  
            @param bytes_out Output bytes transferred.
233  
            @param bytes_out Output bytes transferred.
234  

234  

235  
            @return Coroutine handle to resume immediately.
235  
            @return Coroutine handle to resume immediately.
236  
        */
236  
        */
237  
        virtual std::coroutine_handle<> recv(
237  
        virtual std::coroutine_handle<> recv(
238  
            std::coroutine_handle<> h,
238  
            std::coroutine_handle<> h,
239  
            capy::executor_ref ex,
239  
            capy::executor_ref ex,
240  
            buffer_param buf,
240  
            buffer_param buf,
241  
            int flags,
241  
            int flags,
242  
            std::stop_token token,
242  
            std::stop_token token,
243  
            std::error_code* ec,
243  
            std::error_code* ec,
244  
            std::size_t* bytes_out) = 0;
244  
            std::size_t* bytes_out) = 0;
245  
    };
245  
    };
246  

246  

247  
    /** Represent the awaitable returned by @ref send_to.
247  
    /** Represent the awaitable returned by @ref send_to.
248  

248  

249  
        Captures the destination endpoint and buffer, then dispatches
249  
        Captures the destination endpoint and buffer, then dispatches
250  
        to the backend implementation on suspension.
250  
        to the backend implementation on suspension.
251  
    */
251  
    */
252  
    struct send_to_awaitable
252  
    struct send_to_awaitable
253  
        : detail::bytes_op_base<send_to_awaitable>
253  
        : detail::bytes_op_base<send_to_awaitable>
254  
    {
254  
    {
255  
        udp_socket& s_;
255  
        udp_socket& s_;
256  
        buffer_param buf_;
256  
        buffer_param buf_;
257  
        endpoint dest_;
257  
        endpoint dest_;
258  
        int flags_;
258  
        int flags_;
259  

259  

260  
        send_to_awaitable(
260  
        send_to_awaitable(
261  
            udp_socket& s, buffer_param buf,
261  
            udp_socket& s, buffer_param buf,
262  
            endpoint dest, int flags = 0) noexcept
262  
            endpoint dest, int flags = 0) noexcept
263  
            : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
263  
            : s_(s), buf_(buf), dest_(dest), flags_(flags) {}
264  

264  

265  
        std::coroutine_handle<> dispatch(
265  
        std::coroutine_handle<> dispatch(
266  
            std::coroutine_handle<> h, capy::executor_ref ex) const
266  
            std::coroutine_handle<> h, capy::executor_ref ex) const
267  
        {
267  
        {
268  
            return s_.get().send_to(
268  
            return s_.get().send_to(
269  
                h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
269  
                h, ex, buf_, dest_, flags_, token_, &ec_, &bytes_);
270  
        }
270  
        }
271  
    };
271  
    };
272  

272  

273  
    /** Represent the awaitable returned by @ref recv_from.
273  
    /** Represent the awaitable returned by @ref recv_from.
274  

274  

275  
        Captures the source endpoint reference and buffer, then
275  
        Captures the source endpoint reference and buffer, then
276  
        dispatches to the backend implementation on suspension.
276  
        dispatches to the backend implementation on suspension.
277  
    */
277  
    */
278  
    struct recv_from_awaitable
278  
    struct recv_from_awaitable
279  
        : detail::bytes_op_base<recv_from_awaitable>
279  
        : detail::bytes_op_base<recv_from_awaitable>
280  
    {
280  
    {
281  
        udp_socket& s_;
281  
        udp_socket& s_;
282  
        buffer_param buf_;
282  
        buffer_param buf_;
283  
        endpoint& source_;
283  
        endpoint& source_;
284  
        int flags_;
284  
        int flags_;
285  

285  

286  
        recv_from_awaitable(
286  
        recv_from_awaitable(
287  
            udp_socket& s, buffer_param buf,
287  
            udp_socket& s, buffer_param buf,
288  
            endpoint& source, int flags = 0) noexcept
288  
            endpoint& source, int flags = 0) noexcept
289  
            : s_(s), buf_(buf), source_(source), flags_(flags) {}
289  
            : s_(s), buf_(buf), source_(source), flags_(flags) {}
290  

290  

291  
        std::coroutine_handle<> dispatch(
291  
        std::coroutine_handle<> dispatch(
292  
            std::coroutine_handle<> h, capy::executor_ref ex) const
292  
            std::coroutine_handle<> h, capy::executor_ref ex) const
293  
        {
293  
        {
294  
            return s_.get().recv_from(
294  
            return s_.get().recv_from(
295  
                h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
295  
                h, ex, buf_, &source_, flags_, token_, &ec_, &bytes_);
296  
        }
296  
        }
297  
    };
297  
    };
298  

298  

299  
    /// Represent the awaitable returned by @ref connect.
299  
    /// Represent the awaitable returned by @ref connect.
300  
    struct connect_awaitable
300  
    struct connect_awaitable
301  
        : detail::void_op_base<connect_awaitable>
301  
        : detail::void_op_base<connect_awaitable>
302  
    {
302  
    {
303  
        udp_socket& s_;
303  
        udp_socket& s_;
304  
        endpoint endpoint_;
304  
        endpoint endpoint_;
305  

305  

306  
        connect_awaitable(udp_socket& s, endpoint ep) noexcept
306  
        connect_awaitable(udp_socket& s, endpoint ep) noexcept
307  
            : s_(s), endpoint_(ep) {}
307  
            : s_(s), endpoint_(ep) {}
308  

308  

309  
        std::coroutine_handle<> dispatch(
309  
        std::coroutine_handle<> dispatch(
310  
            std::coroutine_handle<> h, capy::executor_ref ex) const
310  
            std::coroutine_handle<> h, capy::executor_ref ex) const
311  
        {
311  
        {
312  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
312  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
313  
        }
313  
        }
314  
    };
314  
    };
315  

315  

316  
    /// Represent the awaitable returned by @ref send.
316  
    /// Represent the awaitable returned by @ref send.
317  
    struct send_awaitable
317  
    struct send_awaitable
318  
        : detail::bytes_op_base<send_awaitable>
318  
        : detail::bytes_op_base<send_awaitable>
319  
    {
319  
    {
320  
        udp_socket& s_;
320  
        udp_socket& s_;
321  
        buffer_param buf_;
321  
        buffer_param buf_;
322  
        int flags_;
322  
        int flags_;
323  

323  

324  
        send_awaitable(
324  
        send_awaitable(
325  
            udp_socket& s, buffer_param buf,
325  
            udp_socket& s, buffer_param buf,
326  
            int flags = 0) noexcept
326  
            int flags = 0) noexcept
327  
            : s_(s), buf_(buf), flags_(flags) {}
327  
            : s_(s), buf_(buf), flags_(flags) {}
328  

328  

329  
        std::coroutine_handle<> dispatch(
329  
        std::coroutine_handle<> dispatch(
330  
            std::coroutine_handle<> h, capy::executor_ref ex) const
330  
            std::coroutine_handle<> h, capy::executor_ref ex) const
331  
        {
331  
        {
332  
            return s_.get().send(
332  
            return s_.get().send(
333  
                h, ex, buf_, flags_, token_, &ec_, &bytes_);
333  
                h, ex, buf_, flags_, token_, &ec_, &bytes_);
334  
        }
334  
        }
335  
    };
335  
    };
336  

336  

337  
    /// Represent the awaitable returned by @ref recv.
337  
    /// Represent the awaitable returned by @ref recv.
338  
    struct recv_awaitable
338  
    struct recv_awaitable
339  
        : detail::bytes_op_base<recv_awaitable>
339  
        : detail::bytes_op_base<recv_awaitable>
340  
    {
340  
    {
341  
        udp_socket& s_;
341  
        udp_socket& s_;
342  
        buffer_param buf_;
342  
        buffer_param buf_;
343  
        int flags_;
343  
        int flags_;
344  

344  

345  
        recv_awaitable(
345  
        recv_awaitable(
346  
            udp_socket& s, buffer_param buf,
346  
            udp_socket& s, buffer_param buf,
347  
            int flags = 0) noexcept
347  
            int flags = 0) noexcept
348  
            : s_(s), buf_(buf), flags_(flags) {}
348  
            : s_(s), buf_(buf), flags_(flags) {}
349  

349  

350  
        std::coroutine_handle<> dispatch(
350  
        std::coroutine_handle<> dispatch(
351  
            std::coroutine_handle<> h, capy::executor_ref ex) const
351  
            std::coroutine_handle<> h, capy::executor_ref ex) const
352  
        {
352  
        {
353  
            return s_.get().recv(
353  
            return s_.get().recv(
354  
                h, ex, buf_, flags_, token_, &ec_, &bytes_);
354  
                h, ex, buf_, flags_, token_, &ec_, &bytes_);
355  
        }
355  
        }
356  
    };
356  
    };
357  

357  

358  
public:
358  
public:
359  
    /** Destructor.
359  
    /** Destructor.
360  

360  

361  
        Closes the socket if open, cancelling any pending operations.
361  
        Closes the socket if open, cancelling any pending operations.
362  
    */
362  
    */
363  
    ~udp_socket() override;
363  
    ~udp_socket() override;
364  

364  

365  
    /** Construct a socket from an execution context.
365  
    /** Construct a socket from an execution context.
366  

366  

367  
        @param ctx The execution context that will own this socket.
367  
        @param ctx The execution context that will own this socket.
368  
    */
368  
    */
369  
    explicit udp_socket(capy::execution_context& ctx);
369  
    explicit udp_socket(capy::execution_context& ctx);
370  

370  

371  
    /** Construct a socket from an executor.
371  
    /** Construct a socket from an executor.
372  

372  

373  
        The socket is associated with the executor's context.
373  
        The socket is associated with the executor's context.
374  

374  

375  
        @param ex The executor whose context will own the socket.
375  
        @param ex The executor whose context will own the socket.
376  
    */
376  
    */
377  
    template<class Ex>
377  
    template<class Ex>
378  
        requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
378  
        requires(!std::same_as<std::remove_cvref_t<Ex>, udp_socket>) &&
379  
        capy::Executor<Ex>
379  
        capy::Executor<Ex>
380  
    explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
380  
    explicit udp_socket(Ex const& ex) : udp_socket(ex.context())
381  
    {
381  
    {
382  
    }
382  
    }
383  

383  

384  
    /** Move constructor.
384  
    /** Move constructor.
385  

385  

386  
        Transfers ownership of the socket resources.
386  
        Transfers ownership of the socket resources.
387  

387  

388  
        @param other The socket to move from.
388  
        @param other The socket to move from.
389  
    */
389  
    */
390  
    udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
390  
    udp_socket(udp_socket&& other) noexcept : io_object(std::move(other)) {}
391  

391  

392  
    /** Move assignment operator.
392  
    /** Move assignment operator.
393  

393  

394  
        Closes any existing socket and transfers ownership.
394  
        Closes any existing socket and transfers ownership.
395  

395  

396  
        @param other The socket to move from.
396  
        @param other The socket to move from.
397  
        @return Reference to this socket.
397  
        @return Reference to this socket.
398  
    */
398  
    */
399  
    udp_socket& operator=(udp_socket&& other) noexcept
399  
    udp_socket& operator=(udp_socket&& other) noexcept
400  
    {
400  
    {
401  
        if (this != &other)
401  
        if (this != &other)
402  
        {
402  
        {
403  
            close();
403  
            close();
404  
            h_ = std::move(other.h_);
404  
            h_ = std::move(other.h_);
405  
        }
405  
        }
406  
        return *this;
406  
        return *this;
407  
    }
407  
    }
408  

408  

409  
    udp_socket(udp_socket const&)            = delete;
409  
    udp_socket(udp_socket const&)            = delete;
410  
    udp_socket& operator=(udp_socket const&) = delete;
410  
    udp_socket& operator=(udp_socket const&) = delete;
411  

411  

412  
    /** Open the socket.
412  
    /** Open the socket.
413  

413  

414  
        Creates a UDP socket and associates it with the platform
414  
        Creates a UDP socket and associates it with the platform
415  
        reactor.
415  
        reactor.
416  

416  

417  
        @param proto The protocol (IPv4 or IPv6). Defaults to
417  
        @param proto The protocol (IPv4 or IPv6). Defaults to
418  
            `udp::v4()`.
418  
            `udp::v4()`.
419  

419  

420  
        @throws std::system_error on failure.
420  
        @throws std::system_error on failure.
421  
    */
421  
    */
422  
    void open(udp proto = udp::v4());
422  
    void open(udp proto = udp::v4());
423  

423  

424  
    /** Close the socket.
424  
    /** Close the socket.
425  

425  

426  
        Releases socket resources. Any pending operations complete
426  
        Releases socket resources. Any pending operations complete
427  
        with `errc::operation_canceled`.
427  
        with `errc::operation_canceled`.
428  
    */
428  
    */
429  
    void close();
429  
    void close();
430  

430  

431  
    /** Check if the socket is open.
431  
    /** Check if the socket is open.
432  

432  

433  
        @return `true` if the socket is open and ready for operations.
433  
        @return `true` if the socket is open and ready for operations.
434  
    */
434  
    */
435  
    bool is_open() const noexcept
435  
    bool is_open() const noexcept
436  
    {
436  
    {
437  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
437  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
438  
        return h_ && get().native_handle() != ~native_handle_type(0);
438  
        return h_ && get().native_handle() != ~native_handle_type(0);
439  
#else
439  
#else
440  
        return h_ && get().native_handle() >= 0;
440  
        return h_ && get().native_handle() >= 0;
441  
#endif
441  
#endif
442  
    }
442  
    }
443  

443  

444  
    /** Bind the socket to a local endpoint.
444  
    /** Bind the socket to a local endpoint.
445  

445  

446  
        Associates the socket with a local address and port.
446  
        Associates the socket with a local address and port.
447  
        Required before calling `recv_from`.
447  
        Required before calling `recv_from`.
448  

448  

449  
        @param ep The local endpoint to bind to.
449  
        @param ep The local endpoint to bind to.
450  

450  

451  
        @return Error code on failure, empty on success.
451  
        @return Error code on failure, empty on success.
452  

452  

453  
        @throws std::logic_error if the socket is not open.
453  
        @throws std::logic_error if the socket is not open.
454  
    */
454  
    */
455  
    [[nodiscard]] std::error_code bind(endpoint ep);
455  
    [[nodiscard]] std::error_code bind(endpoint ep);
456  

456  

457  
    /** Cancel any pending asynchronous operations.
457  
    /** Cancel any pending asynchronous operations.
458  

458  

459  
        All outstanding operations complete with
459  
        All outstanding operations complete with
460  
        `errc::operation_canceled`. Check `ec == cond::canceled`
460  
        `errc::operation_canceled`. Check `ec == cond::canceled`
461  
        for portable comparison.
461  
        for portable comparison.
462  
    */
462  
    */
463  
    void cancel();
463  
    void cancel();
464  

464  

465  
    /** Get the native socket handle.
465  
    /** Get the native socket handle.
466  

466  

467  
        @return The native socket handle, or -1 if not open.
467  
        @return The native socket handle, or -1 if not open.
468  
    */
468  
    */
469  
    native_handle_type native_handle() const noexcept;
469  
    native_handle_type native_handle() const noexcept;
470  

470  

471  
    /** Set a socket option.
471  
    /** Set a socket option.
472  

472  

473  
        @param opt The option to set.
473  
        @param opt The option to set.
474  

474  

475  
        @throws std::logic_error if the socket is not open.
475  
        @throws std::logic_error if the socket is not open.
476  
        @throws std::system_error on failure.
476  
        @throws std::system_error on failure.
477  
    */
477  
    */
478  
    template<class Option>
478  
    template<class Option>
479  
    void set_option(Option const& opt)
479  
    void set_option(Option const& opt)
480  
    {
480  
    {
481  
        if (!is_open())
481  
        if (!is_open())
482  
            detail::throw_logic_error("set_option: socket not open");
482  
            detail::throw_logic_error("set_option: socket not open");
483  
        std::error_code ec = get().set_option(
483  
        std::error_code ec = get().set_option(
484  
            Option::level(), Option::name(), opt.data(), opt.size());
484  
            Option::level(), Option::name(), opt.data(), opt.size());
485  
        if (ec)
485  
        if (ec)
486  
            detail::throw_system_error(ec, "udp_socket::set_option");
486  
            detail::throw_system_error(ec, "udp_socket::set_option");
487  
    }
487  
    }
488  

488  

489  
    /** Get a socket option.
489  
    /** Get a socket option.
490  

490  

491  
        @return The current option value.
491  
        @return The current option value.
492  

492  

493  
        @throws std::logic_error if the socket is not open.
493  
        @throws std::logic_error if the socket is not open.
494  
        @throws std::system_error on failure.
494  
        @throws std::system_error on failure.
495  
    */
495  
    */
496  
    template<class Option>
496  
    template<class Option>
497  
    Option get_option() const
497  
    Option get_option() const
498  
    {
498  
    {
499  
        if (!is_open())
499  
        if (!is_open())
500  
            detail::throw_logic_error("get_option: socket not open");
500  
            detail::throw_logic_error("get_option: socket not open");
501  
        Option opt{};
501  
        Option opt{};
502  
        std::size_t sz = opt.size();
502  
        std::size_t sz = opt.size();
503  
        std::error_code ec =
503  
        std::error_code ec =
504  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
504  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
505  
        if (ec)
505  
        if (ec)
506  
            detail::throw_system_error(ec, "udp_socket::get_option");
506  
            detail::throw_system_error(ec, "udp_socket::get_option");
507  
        opt.resize(sz);
507  
        opt.resize(sz);
508  
        return opt;
508  
        return opt;
509  
    }
509  
    }
510  

510  

511  
    /** Get the local endpoint of the socket.
511  
    /** Get the local endpoint of the socket.
512  

512  

513  
        @return The local endpoint, or a default endpoint if not bound.
513  
        @return The local endpoint, or a default endpoint if not bound.
514  
    */
514  
    */
515  
    endpoint local_endpoint() const noexcept;
515  
    endpoint local_endpoint() const noexcept;
516  

516  

517  
    /** Send a datagram to the specified destination.
517  
    /** Send a datagram to the specified destination.
518  

518  

519  
        @param buf The buffer containing data to send.
519  
        @param buf The buffer containing data to send.
520  
        @param dest The destination endpoint.
520  
        @param dest The destination endpoint.
521  
        @param flags Message flags (e.g. message_flags::dont_route).
521  
        @param flags Message flags (e.g. message_flags::dont_route).
522  

522  

523  
        @return An awaitable that completes with
523  
        @return An awaitable that completes with
524  
            `io_result<std::size_t>`.
524  
            `io_result<std::size_t>`.
525  

525  

526  
        @throws std::logic_error if the socket is not open.
526  
        @throws std::logic_error if the socket is not open.
527  
    */
527  
    */
528  
    template<capy::ConstBufferSequence Buffers>
528  
    template<capy::ConstBufferSequence Buffers>
529  
    auto send_to(
529  
    auto send_to(
530  
        Buffers const& buf,
530  
        Buffers const& buf,
531  
        endpoint dest,
531  
        endpoint dest,
532  
        corosio::message_flags flags)
532  
        corosio::message_flags flags)
533  
    {
533  
    {
534  
        if (!is_open())
534  
        if (!is_open())
535  
            detail::throw_logic_error("send_to: socket not open");
535  
            detail::throw_logic_error("send_to: socket not open");
536  
        return send_to_awaitable(
536  
        return send_to_awaitable(
537  
            *this, buf, dest, static_cast<int>(flags));
537  
            *this, buf, dest, static_cast<int>(flags));
538  
    }
538  
    }
539  

539  

540  
    /// @overload
540  
    /// @overload
541  
    template<capy::ConstBufferSequence Buffers>
541  
    template<capy::ConstBufferSequence Buffers>
542  
    auto send_to(Buffers const& buf, endpoint dest)
542  
    auto send_to(Buffers const& buf, endpoint dest)
543  
    {
543  
    {
544  
        return send_to(buf, dest, corosio::message_flags::none);
544  
        return send_to(buf, dest, corosio::message_flags::none);
545  
    }
545  
    }
546  

546  

547  
    /** Receive a datagram and capture the sender's endpoint.
547  
    /** Receive a datagram and capture the sender's endpoint.
548  

548  

549  
        @param buf The buffer to receive data into.
549  
        @param buf The buffer to receive data into.
550  
        @param source Reference to an endpoint that will be set to
550  
        @param source Reference to an endpoint that will be set to
551  
            the sender's address on successful completion.
551  
            the sender's address on successful completion.
552  
        @param flags Message flags (e.g. message_flags::peek).
552  
        @param flags Message flags (e.g. message_flags::peek).
553  

553  

554  
        @return An awaitable that completes with
554  
        @return An awaitable that completes with
555  
            `io_result<std::size_t>`.
555  
            `io_result<std::size_t>`.
556  

556  

557  
        @throws std::logic_error if the socket is not open.
557  
        @throws std::logic_error if the socket is not open.
558  
    */
558  
    */
559  
    template<capy::MutableBufferSequence Buffers>
559  
    template<capy::MutableBufferSequence Buffers>
560  
    auto recv_from(
560  
    auto recv_from(
561  
        Buffers const& buf,
561  
        Buffers const& buf,
562  
        endpoint& source,
562  
        endpoint& source,
563  
        corosio::message_flags flags)
563  
        corosio::message_flags flags)
564  
    {
564  
    {
565  
        if (!is_open())
565  
        if (!is_open())
566  
            detail::throw_logic_error("recv_from: socket not open");
566  
            detail::throw_logic_error("recv_from: socket not open");
567  
        return recv_from_awaitable(
567  
        return recv_from_awaitable(
568  
            *this, buf, source, static_cast<int>(flags));
568  
            *this, buf, source, static_cast<int>(flags));
569  
    }
569  
    }
570  

570  

571  
    /// @overload
571  
    /// @overload
572  
    template<capy::MutableBufferSequence Buffers>
572  
    template<capy::MutableBufferSequence Buffers>
573  
    auto recv_from(Buffers const& buf, endpoint& source)
573  
    auto recv_from(Buffers const& buf, endpoint& source)
574  
    {
574  
    {
575  
        return recv_from(buf, source, corosio::message_flags::none);
575  
        return recv_from(buf, source, corosio::message_flags::none);
576  
    }
576  
    }
577  

577  

578  
    /** Initiate an asynchronous connect to set the default peer.
578  
    /** Initiate an asynchronous connect to set the default peer.
579  

579  

580  
        If the socket is not already open, it is opened automatically
580  
        If the socket is not already open, it is opened automatically
581  
        using the address family of @p ep.
581  
        using the address family of @p ep.
582  

582  

583  
        @param ep The remote endpoint to connect to.
583  
        @param ep The remote endpoint to connect to.
584  

584  

585  
        @return An awaitable that completes with `io_result<>`.
585  
        @return An awaitable that completes with `io_result<>`.
586  

586  

587  
        @throws std::system_error if the socket needs to be opened
587  
        @throws std::system_error if the socket needs to be opened
588  
            and the open fails.
588  
            and the open fails.
589  
    */
589  
    */
590  
    auto connect(endpoint ep)
590  
    auto connect(endpoint ep)
591  
    {
591  
    {
592  
        if (!is_open())
592  
        if (!is_open())
593  
            open(ep.is_v6() ? udp::v6() : udp::v4());
593  
            open(ep.is_v6() ? udp::v6() : udp::v4());
594  
        return connect_awaitable(*this, ep);
594  
        return connect_awaitable(*this, ep);
595  
    }
595  
    }
596  

596  

597  
    /** Send a datagram to the connected peer.
597  
    /** Send a datagram to the connected peer.
598  

598  

599  
        @param buf The buffer containing data to send.
599  
        @param buf The buffer containing data to send.
600  
        @param flags Message flags.
600  
        @param flags Message flags.
601  

601  

602  
        @return An awaitable that completes with
602  
        @return An awaitable that completes with
603  
            `io_result<std::size_t>`.
603  
            `io_result<std::size_t>`.
604  

604  

605  
        @throws std::logic_error if the socket is not open.
605  
        @throws std::logic_error if the socket is not open.
606  
    */
606  
    */
607  
    template<capy::ConstBufferSequence Buffers>
607  
    template<capy::ConstBufferSequence Buffers>
608  
    auto send(Buffers const& buf, corosio::message_flags flags)
608  
    auto send(Buffers const& buf, corosio::message_flags flags)
609  
    {
609  
    {
610  
        if (!is_open())
610  
        if (!is_open())
611  
            detail::throw_logic_error("send: socket not open");
611  
            detail::throw_logic_error("send: socket not open");
612  
        return send_awaitable(
612  
        return send_awaitable(
613  
            *this, buf, static_cast<int>(flags));
613  
            *this, buf, static_cast<int>(flags));
614  
    }
614  
    }
615  

615  

616  
    /// @overload
616  
    /// @overload
617  
    template<capy::ConstBufferSequence Buffers>
617  
    template<capy::ConstBufferSequence Buffers>
618  
    auto send(Buffers const& buf)
618  
    auto send(Buffers const& buf)
619  
    {
619  
    {
620  
        return send(buf, corosio::message_flags::none);
620  
        return send(buf, corosio::message_flags::none);
621  
    }
621  
    }
622  

622  

623  
    /** Receive a datagram from the connected peer.
623  
    /** Receive a datagram from the connected peer.
624  

624  

625  
        @param buf The buffer to receive data into.
625  
        @param buf The buffer to receive data into.
626  
        @param flags Message flags (e.g. message_flags::peek).
626  
        @param flags Message flags (e.g. message_flags::peek).
627  

627  

628  
        @return An awaitable that completes with
628  
        @return An awaitable that completes with
629  
            `io_result<std::size_t>`.
629  
            `io_result<std::size_t>`.
630  

630  

631  
        @throws std::logic_error if the socket is not open.
631  
        @throws std::logic_error if the socket is not open.
632  
    */
632  
    */
633  
    template<capy::MutableBufferSequence Buffers>
633  
    template<capy::MutableBufferSequence Buffers>
634  
    auto recv(Buffers const& buf, corosio::message_flags flags)
634  
    auto recv(Buffers const& buf, corosio::message_flags flags)
635  
    {
635  
    {
636  
        if (!is_open())
636  
        if (!is_open())
637  
            detail::throw_logic_error("recv: socket not open");
637  
            detail::throw_logic_error("recv: socket not open");
638  
        return recv_awaitable(
638  
        return recv_awaitable(
639  
            *this, buf, static_cast<int>(flags));
639  
            *this, buf, static_cast<int>(flags));
640  
    }
640  
    }
641  

641  

642  
    /// @overload
642  
    /// @overload
643  
    template<capy::MutableBufferSequence Buffers>
643  
    template<capy::MutableBufferSequence Buffers>
644  
    auto recv(Buffers const& buf)
644  
    auto recv(Buffers const& buf)
645  
    {
645  
    {
646  
        return recv(buf, corosio::message_flags::none);
646  
        return recv(buf, corosio::message_flags::none);
647  
    }
647  
    }
648  

648  

649  
    /** Get the remote endpoint of the socket.
649  
    /** Get the remote endpoint of the socket.
650  

650  

651  
        Returns the address and port of the connected peer.
651  
        Returns the address and port of the connected peer.
652  

652  

653  
        @return The remote endpoint, or a default endpoint if
653  
        @return The remote endpoint, or a default endpoint if
654  
            not connected.
654  
            not connected.
655  
    */
655  
    */
656  
    endpoint remote_endpoint() const noexcept;
656  
    endpoint remote_endpoint() const noexcept;
657  

657  

658  
protected:
658  
protected:
659  
    /// Construct from a pre-built handle (for native_udp_socket).
659  
    /// Construct from a pre-built handle (for native_udp_socket).
660  
    explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
660  
    explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
661  
    {
661  
    {
662  
    }
662  
    }
663  

663  

664  
private:
664  
private:
665  
    /// Open the socket for the given protocol triple.
665  
    /// Open the socket for the given protocol triple.
666  
    void open_for_family(int family, int type, int protocol);
666  
    void open_for_family(int family, int type, int protocol);
667  

667  

668  
    inline implementation& get() const noexcept
668  
    inline implementation& get() const noexcept
669  
    {
669  
    {
670  
        return *static_cast<implementation*>(h_.get());
670  
        return *static_cast<implementation*>(h_.get());
671  
    }
671  
    }
672  
};
672  
};
673  

673  

674  
} // namespace boost::corosio
674  
} // namespace boost::corosio
675  

675  

676  
#endif // BOOST_COROSIO_UDP_SOCKET_HPP
676  
#endif // BOOST_COROSIO_UDP_SOCKET_HPP