include/boost/corosio/tcp_socket.hpp

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