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