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