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