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