include/boost/corosio/tcp_socket.hpp

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