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