TLA Line data 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 HIT 73 : send_to_awaitable(
287 : udp_socket& s,
288 : buffer_param buf,
289 : endpoint dest,
290 : int flags = 0) noexcept
291 146 : : s_(s)
292 73 : , buf_(buf)
293 73 : , dest_(dest)
294 73 : , flags_(flags)
295 : {
296 73 : }
297 :
298 : std::coroutine_handle<>
299 69 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
300 : {
301 138 : return s_.get().send_to(
302 138 : 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 95 : recv_from_awaitable(
319 : udp_socket& s,
320 : buffer_param buf,
321 : endpoint& source,
322 : int flags = 0) noexcept
323 190 : : s_(s)
324 95 : , buf_(buf)
325 95 : , source_(source)
326 95 : , flags_(flags)
327 : {
328 95 : }
329 :
330 : std::coroutine_handle<>
331 89 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
332 : {
333 178 : return s_.get().recv_from(
334 178 : 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 44 : connect_awaitable(udp_socket& s, endpoint ep) noexcept
345 88 : : s_(s)
346 44 : , endpoint_(ep)
347 : {
348 44 : }
349 :
350 : std::coroutine_handle<>
351 42 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
352 : {
353 42 : 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 30 : wait_awaitable(udp_socket& s, wait_type w) noexcept : s_(s), w_(w) {}
364 :
365 : std::coroutine_handle<>
366 28 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
367 : {
368 28 : 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 28 : send_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
380 56 : : s_(s)
381 28 : , buf_(buf)
382 28 : , flags_(flags)
383 : {
384 28 : }
385 :
386 : std::coroutine_handle<>
387 24 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
388 : {
389 24 : 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 65 : recv_awaitable(udp_socket& s, buffer_param buf, int flags = 0) noexcept
401 130 : : s_(s)
402 65 : , buf_(buf)
403 65 : , flags_(flags)
404 : {
405 65 : }
406 :
407 : std::coroutine_handle<>
408 61 : dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
409 : {
410 61 : 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 4 : 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 2 : udp_socket& operator=(udp_socket&& other) noexcept
456 : {
457 2 : if (this != &other)
458 : {
459 2 : close();
460 2 : h_ = std::move(other.h_);
461 : }
462 2 : 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 1776 : 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 1776 : 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 97 : void set_option(Option const& opt)
600 : {
601 97 : if (!is_open())
602 2 : detail::throw_system_error(
603 4 : make_error_code(std::errc::bad_file_descriptor),
604 : "udp_socket::set_option");
605 95 : auto const fam = get().family();
606 95 : std::error_code ec = get().set_option(
607 : opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
608 95 : if (ec)
609 6 : detail::throw_system_error(ec, "udp_socket::set_option");
610 89 : }
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 63 : Option get_option() const
621 : {
622 63 : if (!is_open())
623 2 : detail::throw_system_error(
624 4 : make_error_code(std::errc::bad_file_descriptor),
625 : "udp_socket::get_option");
626 61 : Option opt{};
627 61 : auto const fam = get().family();
628 61 : std::size_t sz = opt.size(fam);
629 : std::error_code ec =
630 61 : get().get_option(opt.level(fam), opt.name(fam), opt.data(fam), &sz);
631 61 : if (ec)
632 2 : detail::throw_system_error(ec, "udp_socket::get_option");
633 59 : opt.resize(fam, sz);
634 59 : 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 73 : send_to(Buffers const& buf, endpoint dest, corosio::message_flags flags)
657 : {
658 73 : send_to_awaitable aw(*this, buf, dest, static_cast<int>(flags));
659 73 : if (!is_open())
660 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
661 73 : return aw;
662 : }
663 :
664 : /// @overload
665 : template<capy::ConstBufferSequence Buffers>
666 73 : [[nodiscard]] auto send_to(Buffers const& buf, endpoint dest)
667 : {
668 73 : 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 95 : [[nodiscard]] auto recv_from(
685 : Buffers const& buf, endpoint& source, corosio::message_flags flags)
686 : {
687 95 : recv_from_awaitable aw(*this, buf, source, static_cast<int>(flags));
688 95 : if (!is_open())
689 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
690 95 : return aw;
691 : }
692 :
693 : /// @overload
694 : template<capy::MutableBufferSequence Buffers>
695 92 : [[nodiscard]] auto recv_from(Buffers const& buf, endpoint& source)
696 : {
697 92 : 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 44 : [[nodiscard]] auto connect(endpoint ep)
713 : {
714 44 : connect_awaitable aw(*this, ep);
715 44 : if (!is_open())
716 10 : aw.ec_ = open(ep.address().family());
717 44 : 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 30 : [[nodiscard]] auto wait(wait_type w)
738 : {
739 30 : 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 28 : [[nodiscard]] auto send(Buffers const& buf, corosio::message_flags flags)
754 : {
755 28 : send_awaitable aw(*this, buf, static_cast<int>(flags));
756 28 : if (!is_open())
757 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
758 28 : return aw;
759 : }
760 :
761 : /// @overload
762 : template<capy::ConstBufferSequence Buffers>
763 28 : [[nodiscard]] auto send(Buffers const& buf)
764 : {
765 28 : 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 65 : [[nodiscard]] auto recv(Buffers const& buf, corosio::message_flags flags)
780 : {
781 65 : recv_awaitable aw(*this, buf, static_cast<int>(flags));
782 65 : if (!is_open())
783 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
784 65 : return aw;
785 : }
786 :
787 : /// @overload
788 : template<capy::MutableBufferSequence Buffers>
789 63 : [[nodiscard]] auto recv(Buffers const& buf)
790 : {
791 63 : 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 42 : explicit udp_socket(io_object::handle h) noexcept : io_object(std::move(h))
806 : {
807 42 : }
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 2607 : inline implementation& get() const noexcept
815 : {
816 2607 : return *static_cast<implementation*>(h_.get());
817 : }
818 : };
819 :
820 : } // namespace boost::corosio
821 :
822 : #endif // BOOST_COROSIO_UDP_SOCKET_HPP
|