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