include/boost/corosio/tcp_socket.hpp

100.0% Lines (48 / 48) 100.0% Functions (28 / 28)
tcp_socket.hpp
f(x) Functions (28)
Function Calls Lines Blocks
boost::corosio::tcp_socket::connect_awaitable::connect_awaitable(boost::corosio::tcp_socket&, boost::corosio::endpoint) :189 4401x 100.0% 100.0% boost::corosio::tcp_socket::connect_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :196 4398x 100.0% 80.0% boost::corosio::tcp_socket::wait_awaitable::wait_awaitable(boost::corosio::tcp_socket&, boost::corosio::wait_type) :208 68x 100.0% 100.0% boost::corosio::tcp_socket::wait_awaitable::dispatch(std::__n4861::coroutine_handle<void>, boost::capy::executor_ref) const :211 64x 100.0% 80.0% boost::corosio::tcp_socket::tcp_socket<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :239 1x 100.0% 100.0% boost::corosio::tcp_socket::tcp_socket(boost::corosio::tcp_socket&&) :255 701x 100.0% 100.0% boost::corosio::tcp_socket::operator=(boost::corosio::tcp_socket&&) :272 25x 100.0% 100.0% boost::corosio::tcp_socket::is_open() const :338 28156x 100.0% 100.0% boost::corosio::tcp_socket::connect(boost::corosio::endpoint) :378 4401x 100.0% 100.0% boost::corosio::tcp_socket::wait(boost::corosio::wait_type) :411 68x 100.0% 100.0% void boost::corosio::tcp_socket::set_option<boost::corosio::native_socket_option::boolean<6, 1> >(boost::corosio::native_socket_option::boolean<6, 1> const&) :535 4x 62.5% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::keep_alive>(boost::corosio::socket_option::keep_alive const&) :535 10x 62.5% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::linger>(boost::corosio::socket_option::linger const&) :535 203x 62.5% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :535 27x 87.5% 94.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :535 10x 62.5% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::reuse_address>(boost::corosio::socket_option::reuse_address const&) :535 7x 75.0% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :535 19x 62.5% 75.0% void boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::v6_only>(boost::corosio::socket_option::v6_only const&) :535 8x 75.0% 81.0% boost::corosio::native_socket_option::boolean<6, 1> boost::corosio::tcp_socket::get_option<boost::corosio::native_socket_option::boolean<6, 1> >() const :560 4x 72.7% 78.0% boost::corosio::socket_option::keep_alive boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::keep_alive>() const :560 10x 72.7% 78.0% boost::corosio::socket_option::linger boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::linger>() const :560 12x 72.7% 78.0% boost::corosio::socket_option::no_delay boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::no_delay>() const :560 29x 90.9% 94.0% boost::corosio::socket_option::receive_buffer_size boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :560 12x 72.7% 78.0% boost::corosio::socket_option::reuse_address boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::reuse_address>() const :560 7x 81.8% 78.0% boost::corosio::socket_option::send_buffer_size boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :560 15x 72.7% 78.0% boost::corosio::socket_option::v6_only boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::v6_only>() const :560 8x 81.8% 83.0% boost::corosio::tcp_socket::tcp_socket() :610 55x 100.0% 100.0% boost::corosio::tcp_socket::get() const :621 32664x 100.0% 100.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2026 Steve Gerbino
4 // Copyright (c) 2026 Michael Vandeberg
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/cppalliance/corosio
10 //
11
12 #ifndef BOOST_COROSIO_TCP_SOCKET_HPP
13 #define BOOST_COROSIO_TCP_SOCKET_HPP
14
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_stream.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/shutdown_type.hpp>
25 #include <boost/corosio/tcp.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 TCP socket for coroutine I/O.
43
44 This class provides asynchronous TCP socket operations that return
45 awaitable types. Each operation participates in the affine awaitable
46 protocol, ensuring coroutines resume on the correct executor.
47
48 The socket must be opened before performing I/O operations. Operations
49 support cancellation through `std::stop_token` via the affine protocol,
50 or explicitly through the `cancel()` member function.
51
52 @par Thread Safety
53 Distinct objects: Safe.@n
54 Shared objects: Unsafe. A socket must not have concurrent operations
55 of the same type (e.g., two simultaneous reads). One read and one
56 write may be in flight simultaneously.
57
58 @par Semantics
59 Wraps the platform TCP/IP stack. Operations dispatch to
60 OS socket APIs via the io_context reactor (epoll, IOCP,
61 kqueue). Satisfies @ref capy::Stream.
62
63 @par Example
64 @par !example connect_and_read
65 */
66 class BOOST_COROSIO_DECL tcp_socket : public io_stream
67 {
68 public:
69 /// The endpoint type used by this socket.
70 using endpoint_type = corosio::endpoint;
71
72 using shutdown_type = corosio::shutdown_type;
73 using enum corosio::shutdown_type;
74
75 /** Define backend hooks for TCP socket operations.
76
77 Platform backends (epoll, IOCP, kqueue, select) derive from
78 this to implement socket I/O, connection, and option management.
79 */
80 struct implementation : io_stream::implementation
81 {
82 /** Initiate an asynchronous connect to the given endpoint.
83
84 @param h Coroutine handle to resume on completion.
85 @param ex Executor for dispatching the completion.
86 @param ep The remote endpoint to connect to.
87 @param token Stop token for cancellation.
88 @param ec Output error code.
89
90 @return Coroutine handle to resume immediately.
91 */
92 virtual std::coroutine_handle<> connect(
93 std::coroutine_handle<> h,
94 capy::executor_ref ex,
95 endpoint ep,
96 std::stop_token token,
97 std::error_code* ec) = 0;
98
99 /** Initiate an asynchronous wait for socket readiness.
100
101 Completes when the socket becomes ready for the
102 specified direction, or an error condition is
103 reported. No bytes are transferred.
104
105 @param h Coroutine handle to resume on completion.
106 @param ex Executor for dispatching the completion.
107 @param w The direction to wait on.
108 @param token Stop token for cancellation.
109 @param ec Output error code.
110
111 @return Coroutine handle to resume immediately.
112 */
113 virtual std::coroutine_handle<> wait(
114 std::coroutine_handle<> h,
115 capy::executor_ref ex,
116 wait_type w,
117 std::stop_token token,
118 std::error_code* ec) = 0;
119
120 /** Shut down the socket for the given direction(s).
121
122 @param what The shutdown direction.
123
124 @return Error code on failure, empty on success.
125 */
126 virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
127
128 /// Return the platform socket descriptor.
129 virtual native_handle_type native_handle() const noexcept = 0;
130
131 /** Release ownership of the native socket handle.
132
133 Deregisters the socket from the backend and cancels
134 pending operations without closing the descriptor. The
135 caller takes ownership.
136
137 @return The native handle.
138 */
139 virtual native_handle_type release_socket() noexcept = 0;
140
141 /** Request cancellation of pending asynchronous operations.
142
143 Operations still in flight complete with `operation_canceled`; an
144 operation whose result is already decided reports that result.
145 Check `ec == cond::canceled` for portable comparison.
146 */
147 virtual void cancel() noexcept = 0;
148
149 /** Set a socket option.
150
151 @param level The protocol level (e.g. `SOL_SOCKET`).
152 @param optname The option name (e.g. `SO_KEEPALIVE`).
153 @param data Pointer to the option value.
154 @param size Size of the option value in bytes.
155 @return Error code on failure, empty on success.
156 */
157 virtual std::error_code set_option(
158 int level,
159 int optname,
160 void const* data,
161 std::size_t size) noexcept = 0;
162
163 /** Get a socket option.
164
165 @param level The protocol level (e.g. `SOL_SOCKET`).
166 @param optname The option name (e.g. `SO_KEEPALIVE`).
167 @param data Pointer to receive the option value.
168 @param size On entry, the size of the buffer. On exit,
169 the size of the option value.
170 @return Error code on failure, empty on success.
171 */
172 virtual std::error_code
173 get_option(int level, int optname, void* data, std::size_t* size)
174 const noexcept = 0;
175
176 /// Return the cached local endpoint.
177 virtual endpoint local_endpoint() const noexcept = 0;
178
179 /// Return the cached remote endpoint.
180 virtual endpoint remote_endpoint() const noexcept = 0;
181 };
182
183 /// Represent the awaitable returned by @ref connect.
184 struct connect_awaitable : detail::void_op_base<connect_awaitable>
185 {
186 tcp_socket& s_;
187 endpoint endpoint_;
188
189 4401x connect_awaitable(tcp_socket& s, endpoint ep) noexcept
190 8802x : s_(s)
191 4401x , endpoint_(ep)
192 {
193 4401x }
194
195 std::coroutine_handle<>
196 4398x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
197 {
198 4398x return s_.get().connect(h, ex, endpoint_, token_, &ec_);
199 }
200 };
201
202 /// Represent the awaitable returned by @ref wait.
203 struct wait_awaitable : detail::void_op_base<wait_awaitable>
204 {
205 tcp_socket& s_;
206 wait_type w_;
207
208 68x wait_awaitable(tcp_socket& s, wait_type w) noexcept : s_(s), w_(w) {}
209
210 std::coroutine_handle<>
211 64x dispatch(std::coroutine_handle<> h, capy::executor_ref ex) const
212 {
213 64x return s_.get().wait(h, ex, w_, token_, &ec_);
214 }
215 };
216
217 public:
218 /** Destructor.
219
220 Closes the socket if open, cancelling any pending operations.
221 */
222 ~tcp_socket() override;
223
224 /** Construct a socket from an execution context.
225
226 @param ctx The execution context that will own this socket.
227 */
228 explicit tcp_socket(capy::execution_context& ctx);
229
230 /** Construct a socket from an executor.
231
232 The socket is associated with the executor's context.
233
234 @param ex The executor whose context will own the socket.
235 */
236 template<class Ex>
237 requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
238 capy::Executor<Ex>
239 1x explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
240 {
241 1x }
242
243 /** Move constructor.
244
245 Transfers ownership of the socket resources.
246
247 @param other The socket to move from.
248
249 @pre No awaitables returned by @p other's methods exist.
250 @pre @p other is not referenced as a peer in any outstanding
251 accept awaitable.
252 @pre The execution context associated with @p other must
253 outlive this socket.
254 */
255 701x tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
256
257 /** Move assignment operator.
258
259 Closes any existing socket and transfers ownership.
260
261 @param other The socket to move from.
262
263 @pre No awaitables returned by either `*this` or @p other's
264 methods exist.
265 @pre Neither `*this` nor @p other is referenced as a peer in
266 any outstanding accept awaitable.
267 @pre The execution context associated with @p other must
268 outlive this socket.
269
270 @return Reference to this socket.
271 */
272 25x tcp_socket& operator=(tcp_socket&& other) noexcept
273 {
274 25x if (this != &other)
275 {
276 25x close();
277 25x h_ = std::move(other.h_);
278 }
279 25x return *this;
280 }
281
282 tcp_socket(tcp_socket const&) = delete;
283 tcp_socket& operator=(tcp_socket const&) = delete;
284
285 /** Open the socket.
286
287 Creates a TCP socket and associates it with the platform
288 reactor (IOCP on Windows). Calling @ref connect on a closed
289 socket opens it automatically with the endpoint's address family,
290 so explicit `open()` is only needed when socket options must be
291 set before connecting.
292
293 Failures such as descriptor exhaustion are normal runtime
294 conditions and are reported through the returned error code.
295 Opening an already-open socket is a no-op that reports
296 success.
297
298 @param proto The protocol (IPv4 or IPv6). Defaults to
299 `tcp::v4()`.
300
301 @return The error code, empty on success.
302 */
303 [[nodiscard]] std::error_code open(tcp proto = tcp::v4()) noexcept;
304
305 /** Bind the socket to a local endpoint.
306
307 Associates the socket with a local address and port before
308 connecting. Useful for multi-homed hosts or source-port
309 pinning.
310
311 @param ep The local endpoint to bind to.
312
313 @return An error code indicating success or the reason for
314 failure.
315
316 @par Error Conditions
317 @li `errc::address_in_use`: The endpoint is already in use.
318 @li `errc::address_not_available`: The address is not
319 available on any local interface.
320 @li `errc::permission_denied`: Insufficient privileges to
321 bind to the endpoint (e.g., privileged port).
322
323 A closed socket reports `errc::bad_file_descriptor`.
324 */
325 [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
326
327 /** Close the socket.
328
329 Releases socket resources. Any pending operations complete
330 with `errc::operation_canceled`.
331 */
332 void close() noexcept;
333
334 /** Check if the socket is open.
335
336 @return `true` if the socket is open and ready for operations.
337 */
338 28156x bool is_open() const noexcept
339 {
340 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
341 return h_ && get().native_handle() != ~native_handle_type(0);
342 #else
343 28156x return h_ && get().native_handle() >= 0;
344 #endif
345 }
346
347 /** Initiate an asynchronous connect operation.
348
349 If the socket is not already open, it is opened automatically
350 using the address family of @p ep (IPv4 or IPv6). If the socket
351 is already open, the existing file descriptor is used as-is.
352
353 The operation supports cancellation via `std::stop_token` through
354 the affine awaitable protocol. If the associated stop token is
355 triggered, the operation completes immediately with
356 `errc::operation_canceled`.
357
358 @param ep The remote endpoint to connect to.
359
360 @return An awaitable that completes with `io_result<>`.
361 Returns success (default error_code) on successful connection,
362 or an error code on failure including:
363 - connection_refused: No server listening at endpoint
364 - timed_out: Connection attempt timed out
365 - network_unreachable: No route to host
366 - operation_canceled: Cancelled via stop_token or cancel().
367 Check `ec == cond::canceled` for portable comparison.
368
369 If the socket needs to be opened and the open fails, the
370 awaitable completes immediately with that error.
371
372 @par Preconditions
373 This socket must outlive the returned awaitable.
374
375 @par Example
376 @par !example connect
377 */
378 4401x [[nodiscard]] auto connect(endpoint ep)
379 {
380 4401x connect_awaitable aw(*this, ep);
381 4401x if (!is_open())
382 87x aw.ec_ = open(ep.is_v6() ? tcp::v6() : tcp::v4());
383 4401x return aw;
384 }
385
386 /** Wait for the socket to become ready in a given direction.
387
388 Suspends until the socket is ready for the requested
389 direction, or an error condition is reported. No bytes
390 are transferred — useful for integrating with C libraries
391 that own the I/O on a nonblocking fd and only need
392 readiness notification (e.g. libpq async, libssh).
393
394 The operation supports cancellation via `std::stop_token`
395 through the affine awaitable protocol. If the associated
396 stop token is triggered, the operation completes
397 immediately with `errc::operation_canceled`.
398
399 @param w The wait direction (read, write, or error).
400
401 @return An awaitable that completes with `io_result<>`.
402 On success, no bytes have been consumed from the
403 stream; a subsequent `read_some` (for read waits)
404 returns the available data.
405
406 A closed socket completes with `errc::bad_file_descriptor`.
407
408 @par Preconditions
409 This socket must outlive the returned awaitable.
410 */
411 68x [[nodiscard]] auto wait(wait_type w)
412 {
413 68x return wait_awaitable(*this, w);
414 }
415
416 /** Cancel any pending asynchronous operations.
417
418 Operations still in flight complete with `errc::operation_canceled`;
419 an operation whose result is already decided reports that result.
420 Check `ec == cond::canceled` for portable comparison.
421 */
422 void cancel() noexcept;
423
424 /** Get the native socket handle.
425
426 Returns the underlying platform-specific socket descriptor.
427 On POSIX systems this is an `int` file descriptor.
428 On Windows this is a `SOCKET` handle.
429
430 @return The native socket handle, or -1/INVALID_SOCKET if not open.
431
432 @par Preconditions
433 None. May be called on closed sockets.
434 */
435 native_handle_type native_handle() const noexcept;
436
437 /** Assign an existing native socket to this object.
438
439 Adopts a TCP socket created outside the library — received
440 from another process, inherited, or made natively — and
441 registers it with the backend. The socket must be a stream
442 socket in the `AF_INET` or `AF_INET6` family. Adoption never
443 alters the descriptor's flags or options: on POSIX the fd
444 must already be non-blocking, and on Windows the socket must
445 be overlapped-capable.
446
447 If this object is already open, pending operations complete
448 with `errc::operation_canceled` and the held socket is
449 closed before the new one is adopted.
450
451 @par Exception Safety
452 Strong guarantee on validation failure: the object is
453 unchanged. If backend registration fails, the object either
454 retains its previous socket or is left closed, depending on
455 the backend. In all failure cases the caller retains
456 ownership of `fd`.
457
458 @param fd The native socket to adopt. On success the object
459 owns it and will close it.
460
461 @return The error code, empty on success. Validation and
462 registration failures are normal runtime conditions when
463 adopting foreign descriptors.
464 */
465 [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
466
467 /** Release ownership of the native socket handle.
468
469 Deregisters the socket from the backend and cancels pending
470 operations without closing the descriptor. The caller takes
471 ownership of the returned handle.
472
473 @return The native handle.
474
475 @throws std::system_error `errc::bad_file_descriptor` if the
476 socket is not open.
477
478 @post is_open() == false
479 */
480 native_handle_type release();
481
482 /** Disable sends or receives on the socket.
483
484 TCP connections are full-duplex: each direction (send and receive)
485 operates independently. This function allows you to close one or
486 both directions without destroying the socket.
487
488 @li @ref shutdown_send sends a TCP FIN packet to the peer,
489 signaling that you have no more data to send. You can still
490 receive data until the peer also closes their send direction.
491 This is the most common use case, typically called before
492 close() to ensure graceful connection termination.
493
494 @li @ref shutdown_receive disables reading on the socket. This
495 does NOT send anything to the peer - they are not informed
496 and may continue sending data. Subsequent reads will fail
497 or return end-of-file. Incoming data may be discarded or
498 buffered depending on the operating system.
499
500 @li @ref shutdown_both combines both effects: sends a FIN and
501 disables reading.
502
503 When the peer shuts down their send direction (sends a FIN),
504 subsequent read operations will complete with `capy::cond::eof`.
505 Use the portable condition test rather than comparing error
506 codes directly:
507
508 @par !example shutdown
509
510 Failures such as a peer that already disconnected are
511 normal runtime conditions and are reported through the
512 returned error code. A closed socket reports
513 `errc::bad_file_descriptor`.
514
515 @param what Determines what operations will no longer be allowed.
516
517 @return The error code, empty on success.
518 */
519 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
520
521 /** Set a socket option.
522
523 Applies a type-safe socket option to the underlying socket.
524 The option type encodes the protocol level and option name.
525
526 @par Example
527 @par !example set_option
528
529 @param opt The option to set.
530
531 @throws std::system_error `errc::bad_file_descriptor` if the
532 socket is not open; otherwise thrown on failure.
533 */
534 template<class Option>
535 288x void set_option(Option const& opt)
536 {
537 288x if (!is_open())
538 2x detail::throw_system_error(
539 4x make_error_code(std::errc::bad_file_descriptor),
540 "tcp_socket::set_option");
541 286x std::error_code ec = get().set_option(
542 Option::level(), Option::name(), opt.data(), opt.size());
543 286x if (ec)
544 7x detail::throw_system_error(ec, "tcp_socket::set_option");
545 279x }
546
547 /** Get a socket option.
548
549 Retrieves the current value of a type-safe socket option.
550
551 @par Example
552 @par !example get_option
553
554 @return The current option value.
555
556 @throws std::system_error `errc::bad_file_descriptor` if the
557 socket is not open; otherwise thrown on failure.
558 */
559 template<class Option>
560 97x Option get_option() const
561 {
562 97x if (!is_open())
563 2x detail::throw_system_error(
564 4x make_error_code(std::errc::bad_file_descriptor),
565 "tcp_socket::get_option");
566 95x Option opt{};
567 95x std::size_t sz = opt.size();
568 std::error_code ec =
569 95x get().get_option(Option::level(), Option::name(), opt.data(), &sz);
570 95x if (ec)
571 7x detail::throw_system_error(ec, "tcp_socket::get_option");
572 88x opt.resize(sz);
573 88x return opt;
574 }
575
576 /** Get the local endpoint of the socket.
577
578 Returns the local address and port to which the socket is bound.
579 For a connected socket, this is the local side of the connection.
580 The endpoint is cached when the connection is established.
581
582 @return The local endpoint, or a default endpoint (0.0.0.0:0) if
583 the socket is not connected.
584
585 @par Thread Safety
586 The cached endpoint value is set during connect/accept completion
587 and cleared during close(). This function may be called concurrently
588 with I/O operations, but must not be called concurrently with
589 connect(), accept(), or close().
590 */
591 endpoint local_endpoint() const noexcept;
592
593 /** Get the remote endpoint of the socket.
594
595 Returns the remote address and port to which the socket is connected.
596 The endpoint is cached when the connection is established.
597
598 @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
599 the socket is not connected.
600
601 @par Thread Safety
602 The cached endpoint value is set during connect/accept completion
603 and cleared during close(). This function may be called concurrently
604 with I/O operations, but must not be called concurrently with
605 connect(), accept(), or close().
606 */
607 endpoint remote_endpoint() const noexcept;
608
609 protected:
610 55x tcp_socket() noexcept = default;
611
612 explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
613
614 private:
615 friend class tcp_acceptor;
616
617 /// Open the socket for the given protocol triple.
618 [[nodiscard]] std::error_code
619 open_for_family(int family, int type, int protocol) noexcept;
620
621 32664x inline implementation& get() const noexcept
622 {
623 32664x return *static_cast<implementation*>(h_.get());
624 }
625 };
626
627 } // namespace boost::corosio
628
629 #endif
630