include/boost/corosio/tcp_socket.hpp

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