include/boost/corosio/tcp_socket.hpp

100.0% Lines (45 / 45) 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 8209x 100.0% 100.0% boost::corosio::tcp_socket::connect_awaitable::dispatch(boost::capy::continuation&, boost::capy::executor_ref) const :215 8206x 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 9107x 100.0% 100.0% boost::corosio::tcp_socket::operator=(boost::corosio::tcp_socket&&) :295 31x 100.0% 100.0% boost::corosio::tcp_socket::is_open() const :362 69267x 100.0% 100.0% boost::corosio::tcp_socket::connect(boost::corosio::endpoint) :401 8209x 100.0% 100.0% boost::corosio::tcp_socket::wait(boost::corosio::wait_type) :433 1076x 100.0% 100.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::native_socket_option::boolean<6, 1> >(boost::corosio::native_socket_option::boolean<6, 1> const&) :563 4x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::keep_alive>(boost::corosio::socket_option::keep_alive const&) :563 10x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::linger>(boost::corosio::socket_option::linger const&) :563 6920x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::no_delay>(boost::corosio::socket_option::no_delay const&) :563 27x 100.0% 100.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::receive_buffer_size>(boost::corosio::socket_option::receive_buffer_size const&) :563 10x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::reuse_address>(boost::corosio::socket_option::reuse_address const&) :563 7x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::send_buffer_size>(boost::corosio::socket_option::send_buffer_size const&) :563 19x 83.3% 92.0% std::error_code boost::corosio::tcp_socket::set_option<boost::corosio::socket_option::v6_only>(boost::corosio::socket_option::v6_only const&) :563 8x 83.3% 92.0% std::tuple<std::error_code, 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 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::keep_alive> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::keep_alive>() const :586 10x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::linger> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::linger>() const :586 12x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::no_delay> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::no_delay>() const :586 29x 90.0% 88.0% std::tuple<std::error_code, boost::corosio::socket_option::receive_buffer_size> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::receive_buffer_size>() const :586 12x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::reuse_address> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::reuse_address>() const :586 7x 90.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::send_buffer_size> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::send_buffer_size>() const :586 15x 80.0% 73.0% std::tuple<std::error_code, boost::corosio::socket_option::v6_only> boost::corosio::tcp_socket::get_option<boost::corosio::socket_option::v6_only>() const :586 8x 90.0% 85.0% boost::corosio::tcp_socket::tcp_socket() :635 61x 100.0% 100.0% boost::corosio::tcp_socket::get() const :650 83022x 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/native_handle.hpp>
19 #include <boost/corosio/detail/op_base.hpp>
20 #include <boost/corosio/io/io_stream.hpp>
21 #include <boost/capy/results.hpp>
22 #include <boost/corosio/detail/buffer_param.hpp>
23 #include <boost/corosio/error.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/await_context.hpp>
30 #include <boost/capy/concept/executor.hpp>
31
32 #include <system_error>
33
34 #include <concepts>
35 #include <boost/capy/continuation.hpp>
36
37 #include <coroutine>
38 #include <cstddef>
39 #include <stop_token>
40 #include <type_traits>
41 #include <utility>
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 8209x connect_awaitable(tcp_socket& s, endpoint ep) noexcept
204 16418x : s_(s)
205 8209x , endpoint_(ep)
206 {
207 8209x }
208
209 friend detail::void_op_base<connect_awaitable>;
210
211 tcp_socket& s_;
212 endpoint endpoint_;
213
214 std::coroutine_handle<>
215 8206x dispatch(capy::continuation& cont, capy::executor_ref ex) const
216 {
217 8206x 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 9107x 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 31x tcp_socket& operator=(tcp_socket&& other) noexcept
296 {
297 31x if (this != &other)
298 {
299 31x close();
300 31x h_ = std::move(other.h_);
301 }
302 31x 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 69267x 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 69267x 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 8209x [[nodiscard]] auto connect(endpoint ep)
402 {
403 8209x connect_awaitable aw(*this, ep);
404 8209x if (!is_open())
405 417x aw.ec_ = open(ep.address().family());
406 8209x 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 A closed object reports `errc::bad_file_descriptor` and the
492 returned handle is the invalid sentinel ( -1, or
493 `INVALID_SOCKET` on Windows ), never zero.
494
495 @note With the IOCP backend, a socket released while operations
496 on it are in flight stays bound to this context's completion
497 port for good. Windows refuses to unbind a handle with I/O
498 outstanding, so no context, this one included, can assign it
499 afterwards. Its cancelled operations still complete normally.
500
501 @return The error code and the native handle.
502
503 @post is_open() == false
504 */
505 [[nodiscard]] capy::results<native_handle_type> release() noexcept;
506
507 /** Disable sends or receives on the socket.
508
509 TCP connections are full-duplex: each direction (send and receive)
510 operates independently. This function allows you to close one or
511 both directions without destroying the socket.
512
513 @li @ref shutdown_send sends a TCP FIN packet to the peer,
514 signaling that you have no more data to send. You can still
515 receive data until the peer also closes their send direction.
516 This is the most common use case, typically called before
517 close() to ensure graceful connection termination.
518
519 @li @ref shutdown_receive disables reading on the socket. This
520 does not send anything to the peer. The peer is not informed
521 and may continue sending data. Subsequent reads fail
522 or return end-of-file. Incoming data may be discarded or
523 buffered depending on the operating system.
524
525 @li @ref shutdown_both combines both effects: sends a FIN and
526 disables reading.
527
528 When the peer shuts down their send direction (sends a FIN),
529 subsequent read operations complete with `capy::cond::eof`.
530 Use the portable condition test rather than comparing error
531 codes directly:
532
533 @par !example shutdown
534
535 @par Error Conditions
536 Failures such as a peer that already disconnected are
537 normal runtime conditions and are reported through the
538 returned error code. A closed socket reports
539 `errc::bad_file_descriptor`.
540
541 @param what Determines which operations are no longer allowed.
542
543 @return The error code, empty on success.
544 */
545 [[nodiscard]] std::error_code shutdown(shutdown_type what) noexcept;
546
547 /** Set a socket option.
548
549 Applies a type-safe socket option to the underlying socket.
550 The option type encodes the protocol level and option name.
551
552 Fails with `errc::bad_file_descriptor` if the socket is not
553 open, or with the error the operating system reports.
554
555 @par Example
556 @par !example set_option
557
558 @param opt The option to set.
559
560 @return The error code, empty on success.
561 */
562 template<class Option>
563 7005x [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
564 {
565 7005x if (!is_open())
566 2x return make_error_code(std::errc::bad_file_descriptor);
567 7003x auto const fam = get().family();
568 7003x return get().set_option(
569 7003x opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
570 }
571
572 /** Get a socket option.
573
574 Retrieves the current value of a type-safe socket option.
575
576 Fails with `errc::bad_file_descriptor` if the socket is not
577 open, or with the error the operating system reports. On
578 failure the option is value-initialized.
579
580 @par Example
581 @par !example get_option
582
583 @return The error code and the option value.
584 */
585 template<class Option>
586 97x [[nodiscard]] capy::results<Option> get_option() const noexcept
587 {
588 97x if (!is_open())
589 2x return {make_error_code(std::errc::bad_file_descriptor), Option{}};
590 95x Option opt{};
591 95x auto const fam = get().family();
592 95x std::size_t sz = opt.size(fam);
593 95x if (auto ec = get().get_option(
594 opt.level(fam), opt.name(fam), opt.data(fam), &sz))
595 7x return {ec, Option{}};
596 88x opt.resize(fam, sz);
597 88x return {std::error_code{}, std::move(opt)};
598 }
599
600 /** Get the local endpoint of the socket.
601
602 Returns the local address and port to which the socket is bound.
603 For a connected socket, this is the local side of the connection.
604 The endpoint is cached when the connection is established.
605
606 @return The local endpoint, or a default endpoint (0.0.0.0:0) if
607 the socket is not connected.
608
609 @par Thread Safety
610 The cached endpoint value is set during connect/accept completion
611 and cleared during close(). This function may be called concurrently
612 with I/O operations, but must not be called concurrently with
613 connect(), accept(), or close().
614 */
615 endpoint local_endpoint() const noexcept;
616
617 /** Get the remote endpoint of the socket.
618
619 Returns the remote address and port to which the socket is connected.
620 The endpoint is cached when the connection is established.
621
622 @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
623 the socket is not connected.
624
625 @par Thread Safety
626 The cached endpoint value is set during connect/accept completion
627 and cleared during close(). This function may be called concurrently
628 with I/O operations, but must not be called concurrently with
629 connect(), accept(), or close().
630 */
631 endpoint remote_endpoint() const noexcept;
632
633 protected:
634 /// Default construct a closed socket for a derived class to open.
635 61x tcp_socket() noexcept = default;
636
637 /** Adopt an existing handle.
638
639 @param h The handle the socket takes ownership of.
640 */
641 explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
642
643 private:
644 friend class tcp_acceptor;
645
646 /// Open the socket for the given protocol triple.
647 [[nodiscard]] std::error_code
648 open_for_family(int family, int type, int protocol) noexcept;
649
650 83022x inline implementation& get() const noexcept
651 {
652 83022x return *static_cast<implementation*>(h_.get());
653 }
654 };
655
656 } // namespace boost::corosio
657
658 #endif
659