1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
 
3 +
// Copyright (c) 2026 Steve Gerbino
3  
//
4  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
7  
//
7  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
8  
//
9  
//
9  

10  

10  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
11  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
11  
#define BOOST_COROSIO_TCP_SOCKET_HPP
12  
#define BOOST_COROSIO_TCP_SOCKET_HPP
12  

13  

13  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/detail/except.hpp>
16 -
#include <boost/corosio/io_stream.hpp>
17 +
#include <boost/corosio/io/io_stream.hpp>
17  
#include <boost/capy/io_result.hpp>
18  
#include <boost/capy/io_result.hpp>
18  
#include <boost/corosio/io_buffer_param.hpp>
19  
#include <boost/corosio/io_buffer_param.hpp>
19  
#include <boost/corosio/endpoint.hpp>
20  
#include <boost/corosio/endpoint.hpp>
20  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/executor_ref.hpp>
21  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/execution_context.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/concept/executor.hpp>
24  
#include <boost/capy/concept/executor.hpp>
24  

25  

25  
#include <system_error>
26  
#include <system_error>
26  

27  

27  
#include <concepts>
28  
#include <concepts>
28  
#include <coroutine>
29  
#include <coroutine>
29  
#include <cstddef>
30  
#include <cstddef>
30  
#include <memory>
31  
#include <memory>
31  
#include <stop_token>
32  
#include <stop_token>
32  
#include <type_traits>
33  
#include <type_traits>
33  

34  

34  
namespace boost::corosio {
35  
namespace boost::corosio {
35  

36  

36  
#if BOOST_COROSIO_HAS_IOCP
37  
#if BOOST_COROSIO_HAS_IOCP
37  
using native_handle_type = std::uintptr_t; // SOCKET
38  
using native_handle_type = std::uintptr_t; // SOCKET
38  
#else
39  
#else
39  
using native_handle_type = int;
40  
using native_handle_type = int;
40  
#endif
41  
#endif
41  

42  

42  
/** An asynchronous TCP socket for coroutine I/O.
43  
/** An asynchronous TCP socket for coroutine I/O.
43  

44  

44  
    This class provides asynchronous TCP socket operations that return
45  
    This class provides asynchronous TCP socket operations that return
45  
    awaitable types. Each operation participates in the affine awaitable
46  
    awaitable types. Each operation participates in the affine awaitable
46  
    protocol, ensuring coroutines resume on the correct executor.
47  
    protocol, ensuring coroutines resume on the correct executor.
47  

48  

48  
    The socket must be opened before performing I/O operations. Operations
49  
    The socket must be opened before performing I/O operations. Operations
49  
    support cancellation through `std::stop_token` via the affine protocol,
50  
    support cancellation through `std::stop_token` via the affine protocol,
50  
    or explicitly through the `cancel()` member function.
51  
    or explicitly through the `cancel()` member function.
51  

52  

52  
    @par Thread Safety
53  
    @par Thread Safety
53  
    Distinct objects: Safe.@n
54  
    Distinct objects: Safe.@n
54  
    Shared objects: Unsafe. A socket must not have concurrent operations
55  
    Shared objects: Unsafe. A socket must not have concurrent operations
55  
    of the same type (e.g., two simultaneous reads). One read and one
56  
    of the same type (e.g., two simultaneous reads). One read and one
56  
    write may be in flight simultaneously.
57  
    write may be in flight simultaneously.
57  

58  

58  
    @par Semantics
59  
    @par Semantics
59  
    Wraps the platform TCP/IP stack. Operations dispatch to
60  
    Wraps the platform TCP/IP stack. Operations dispatch to
60  
    OS socket APIs via the io_context reactor (epoll, IOCP,
61  
    OS socket APIs via the io_context reactor (epoll, IOCP,
61  
    kqueue). Satisfies @ref capy::Stream.
62  
    kqueue). Satisfies @ref capy::Stream.
62  

63  

63  
    @par Example
64  
    @par Example
64  
    @code
65  
    @code
65  
    io_context ioc;
66  
    io_context ioc;
66  
    tcp_socket s(ioc);
67  
    tcp_socket s(ioc);
67  
    s.open();
68  
    s.open();
68  

69  

69  
    // Using structured bindings
70  
    // Using structured bindings
70  
    auto [ec] = co_await s.connect(
71  
    auto [ec] = co_await s.connect(
71  
        endpoint(ipv4_address::loopback(), 8080));
72  
        endpoint(ipv4_address::loopback(), 8080));
72  
    if (ec)
73  
    if (ec)
73  
        co_return;
74  
        co_return;
74  

75  

75  
    char buf[1024];
76  
    char buf[1024];
76  
    auto [read_ec, n] = co_await s.read_some(
77  
    auto [read_ec, n] = co_await s.read_some(
77  
        capy::mutable_buffer(buf, sizeof(buf)));
78  
        capy::mutable_buffer(buf, sizeof(buf)));
78  
    @endcode
79  
    @endcode
79  
*/
80  
*/
80  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
81  
class BOOST_COROSIO_DECL tcp_socket : public io_stream
81  
{
82  
{
82  
public:
83  
public:
83  
    /** Different ways a socket may be shutdown. */
84  
    /** Different ways a socket may be shutdown. */
84  
    enum shutdown_type
85  
    enum shutdown_type
85  
    {
86  
    {
86  
        shutdown_receive,
87  
        shutdown_receive,
87  
        shutdown_send,
88  
        shutdown_send,
88  
        shutdown_both
89  
        shutdown_both
89  
    };
90  
    };
90  

91  

91  
    /** Options for SO_LINGER socket option. */
92  
    /** Options for SO_LINGER socket option. */
92  
    struct linger_options
93  
    struct linger_options
93  
    {
94  
    {
94  
        bool enabled = false;
95  
        bool enabled = false;
95 -
        int timeout = 0; // seconds
96 +
        int timeout  = 0; // seconds
96  
    };
97  
    };
97  

98  

98  
    struct implementation : io_stream::implementation
99  
    struct implementation : io_stream::implementation
99  
    {
100  
    {
100  
        virtual std::coroutine_handle<> connect(
101  
        virtual std::coroutine_handle<> connect(
101  
            std::coroutine_handle<>,
102  
            std::coroutine_handle<>,
102  
            capy::executor_ref,
103  
            capy::executor_ref,
103  
            endpoint,
104  
            endpoint,
104  
            std::stop_token,
105  
            std::stop_token,
105  
            std::error_code*) = 0;
106  
            std::error_code*) = 0;
106  

107  

107  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
108  
        virtual std::error_code shutdown(shutdown_type) noexcept = 0;
108  

109  

109  
        virtual native_handle_type native_handle() const noexcept = 0;
110  
        virtual native_handle_type native_handle() const noexcept = 0;
110  

111  

111  
        /** Request cancellation of pending asynchronous operations.
112  
        /** Request cancellation of pending asynchronous operations.
112  

113  

113  
            All outstanding operations complete with operation_canceled error.
114  
            All outstanding operations complete with operation_canceled error.
114  
            Check `ec == cond::canceled` for portable comparison.
115  
            Check `ec == cond::canceled` for portable comparison.
115  
        */
116  
        */
116  
        virtual void cancel() noexcept = 0;
117  
        virtual void cancel() noexcept = 0;
117  

118  

118  
        // Socket options
119  
        // Socket options
119  
        virtual std::error_code set_no_delay(bool value) noexcept = 0;
120  
        virtual std::error_code set_no_delay(bool value) noexcept = 0;
120  
        virtual bool no_delay(std::error_code& ec) const noexcept = 0;
121  
        virtual bool no_delay(std::error_code& ec) const noexcept = 0;
121  

122  

122  
        virtual std::error_code set_keep_alive(bool value) noexcept = 0;
123  
        virtual std::error_code set_keep_alive(bool value) noexcept = 0;
123  
        virtual bool keep_alive(std::error_code& ec) const noexcept = 0;
124  
        virtual bool keep_alive(std::error_code& ec) const noexcept = 0;
124  

125  

125 -
        virtual std::error_code set_receive_buffer_size(int size) noexcept = 0;
126 +
        virtual std::error_code set_receive_buffer_size(int size) noexcept  = 0;
126  
        virtual int receive_buffer_size(std::error_code& ec) const noexcept = 0;
127  
        virtual int receive_buffer_size(std::error_code& ec) const noexcept = 0;
127  

128  

128 -
        virtual std::error_code set_send_buffer_size(int size) noexcept = 0;
129 +
        virtual std::error_code set_send_buffer_size(int size) noexcept  = 0;
129  
        virtual int send_buffer_size(std::error_code& ec) const noexcept = 0;
130  
        virtual int send_buffer_size(std::error_code& ec) const noexcept = 0;
130  

131  

131  
        virtual std::error_code
132  
        virtual std::error_code
132 -
        set_linger(bool enabled, int timeout) noexcept = 0;
133 +
        set_linger(bool enabled, int timeout) noexcept                    = 0;
133  
        virtual linger_options linger(std::error_code& ec) const noexcept = 0;
134  
        virtual linger_options linger(std::error_code& ec) const noexcept = 0;
134  

135  

135  
        /// Returns the cached local endpoint.
136  
        /// Returns the cached local endpoint.
136  
        virtual endpoint local_endpoint() const noexcept = 0;
137  
        virtual endpoint local_endpoint() const noexcept = 0;
137  

138  

138  
        /// Returns the cached remote endpoint.
139  
        /// Returns the cached remote endpoint.
139  
        virtual endpoint remote_endpoint() const noexcept = 0;
140  
        virtual endpoint remote_endpoint() const noexcept = 0;
140  
    };
141  
    };
141  

142  

142  
    struct connect_awaitable
143  
    struct connect_awaitable
143  
    {
144  
    {
144  
        tcp_socket& s_;
145  
        tcp_socket& s_;
145  
        endpoint endpoint_;
146  
        endpoint endpoint_;
146  
        std::stop_token token_;
147  
        std::stop_token token_;
147  
        mutable std::error_code ec_;
148  
        mutable std::error_code ec_;
148  

149  

149  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
150  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
150  
            : s_(s)
151  
            : s_(s)
151  
            , endpoint_(ep)
152  
            , endpoint_(ep)
152  
        {
153  
        {
153  
        }
154  
        }
154  

155  

155  
        bool await_ready() const noexcept
156  
        bool await_ready() const noexcept
156  
        {
157  
        {
157  
            return token_.stop_requested();
158  
            return token_.stop_requested();
158  
        }
159  
        }
159  

160  

160  
        capy::io_result<> await_resume() const noexcept
161  
        capy::io_result<> await_resume() const noexcept
161  
        {
162  
        {
162  
            if (token_.stop_requested())
163  
            if (token_.stop_requested())
163  
                return {make_error_code(std::errc::operation_canceled)};
164  
                return {make_error_code(std::errc::operation_canceled)};
164  
            return {ec_};
165  
            return {ec_};
165  
        }
166  
        }
166  

167  

167  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
168  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
168  
            -> std::coroutine_handle<>
169  
            -> std::coroutine_handle<>
169  
        {
170  
        {
170  
            token_ = env->stop_token;
171  
            token_ = env->stop_token;
171  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
172  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
172  
        }
173  
        }
173  
    };
174  
    };
174  

175  

175  
public:
176  
public:
176  
    /** Destructor.
177  
    /** Destructor.
177  

178  

178  
        Closes the socket if open, cancelling any pending operations.
179  
        Closes the socket if open, cancelling any pending operations.
179  
    */
180  
    */
180  
    ~tcp_socket() override;
181  
    ~tcp_socket() override;
181  

182  

182  
    /** Construct a socket from an execution context.
183  
    /** Construct a socket from an execution context.
183  

184  

184  
        @param ctx The execution context that will own this socket.
185  
        @param ctx The execution context that will own this socket.
185  
    */
186  
    */
186  
    explicit tcp_socket(capy::execution_context& ctx);
187  
    explicit tcp_socket(capy::execution_context& ctx);
187  

188  

188  
    /** Construct a socket from an executor.
189  
    /** Construct a socket from an executor.
189  

190  

190  
        The socket is associated with the executor's context.
191  
        The socket is associated with the executor's context.
191  

192  

192  
        @param ex The executor whose context will own the socket.
193  
        @param ex The executor whose context will own the socket.
193  
    */
194  
    */
194  
    template<class Ex>
195  
    template<class Ex>
195  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
196  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
196  
        capy::Executor<Ex>
197  
        capy::Executor<Ex>
197  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
198  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
198  
    {
199  
    {
199  
    }
200  
    }
200  

201  

201  
    /** Move constructor.
202  
    /** Move constructor.
202  

203  

203  
        Transfers ownership of the socket resources.
204  
        Transfers ownership of the socket resources.
204  

205  

205  
        @param other The socket to move from.
206  
        @param other The socket to move from.
206  
    */
207  
    */
207 -
    tcp_socket(tcp_socket&& other) noexcept : io_stream(std::move(other)) {}
208 +
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
208  

209  

209  
    /** Move assignment operator.
210  
    /** Move assignment operator.
210  

211  

211  
        Closes any existing socket and transfers ownership.
212  
        Closes any existing socket and transfers ownership.
212  
        @param other The socket to move from.
213  
        @param other The socket to move from.
213  

214  

214  
        @return Reference to this socket.
215  
        @return Reference to this socket.
215  
    */
216  
    */
216  
    tcp_socket& operator=(tcp_socket&& other) noexcept
217  
    tcp_socket& operator=(tcp_socket&& other) noexcept
217  
    {
218  
    {
218  
        if (this != &other)
219  
        if (this != &other)
219  
        {
220  
        {
220  
            close();
221  
            close();
221  
            h_ = std::move(other.h_);
222  
            h_ = std::move(other.h_);
222  
        }
223  
        }
223  
        return *this;
224  
        return *this;
224  
    }
225  
    }
225  

226  

226 -
    tcp_socket(tcp_socket const&) = delete;
227 +
    tcp_socket(tcp_socket const&)            = delete;
227  
    tcp_socket& operator=(tcp_socket const&) = delete;
228  
    tcp_socket& operator=(tcp_socket const&) = delete;
228  

229  

229  
    /** Open the socket.
230  
    /** Open the socket.
230  

231  

231  
        Creates an IPv4 TCP socket and associates it with the platform
232  
        Creates an IPv4 TCP socket and associates it with the platform
232  
        reactor (IOCP on Windows). This must be called before initiating
233  
        reactor (IOCP on Windows). This must be called before initiating
233  
        I/O operations.
234  
        I/O operations.
234  

235  

235  
        @throws std::system_error on failure.
236  
        @throws std::system_error on failure.
236  
    */
237  
    */
237  
    void open();
238  
    void open();
238  

239  

239  
    /** Close the socket.
240  
    /** Close the socket.
240  

241  

241  
        Releases socket resources. Any pending operations complete
242  
        Releases socket resources. Any pending operations complete
242  
        with `errc::operation_canceled`.
243  
        with `errc::operation_canceled`.
243  
    */
244  
    */
244  
    void close();
245  
    void close();
245  

246  

246  
    /** Check if the socket is open.
247  
    /** Check if the socket is open.
247  

248  

248  
        @return `true` if the socket is open and ready for operations.
249  
        @return `true` if the socket is open and ready for operations.
249  
    */
250  
    */
250  
    bool is_open() const noexcept
251  
    bool is_open() const noexcept
251  
    {
252  
    {
252  
#if BOOST_COROSIO_HAS_IOCP
253  
#if BOOST_COROSIO_HAS_IOCP
253  
        return h_ && get().native_handle() != ~native_handle_type(0);
254  
        return h_ && get().native_handle() != ~native_handle_type(0);
254  
#else
255  
#else
255  
        return h_ && get().native_handle() >= 0;
256  
        return h_ && get().native_handle() >= 0;
256  
#endif
257  
#endif
257  
    }
258  
    }
258  

259  

259  
    /** Initiate an asynchronous connect operation.
260  
    /** Initiate an asynchronous connect operation.
260  

261  

261  
        Connects the socket to the specified remote endpoint. The socket
262  
        Connects the socket to the specified remote endpoint. The socket
262  
        must be open before calling this function.
263  
        must be open before calling this function.
263  

264  

264  
        The operation supports cancellation via `std::stop_token` through
265  
        The operation supports cancellation via `std::stop_token` through
265  
        the affine awaitable protocol. If the associated stop token is
266  
        the affine awaitable protocol. If the associated stop token is
266  
        triggered, the operation completes immediately with
267  
        triggered, the operation completes immediately with
267  
        `errc::operation_canceled`.
268  
        `errc::operation_canceled`.
268  

269  

269  
        @param ep The remote endpoint to connect to.
270  
        @param ep The remote endpoint to connect to.
270  

271  

271  
        @return An awaitable that completes with `io_result<>`.
272  
        @return An awaitable that completes with `io_result<>`.
272  
            Returns success (default error_code) on successful connection,
273  
            Returns success (default error_code) on successful connection,
273  
            or an error code on failure including:
274  
            or an error code on failure including:
274  
            - connection_refused: No server listening at endpoint
275  
            - connection_refused: No server listening at endpoint
275  
            - timed_out: Connection attempt timed out
276  
            - timed_out: Connection attempt timed out
276  
            - network_unreachable: No route to host
277  
            - network_unreachable: No route to host
277  
            - operation_canceled: Cancelled via stop_token or cancel().
278  
            - operation_canceled: Cancelled via stop_token or cancel().
278  
                Check `ec == cond::canceled` for portable comparison.
279  
                Check `ec == cond::canceled` for portable comparison.
279  

280  

280  
        @throws std::logic_error if the socket is not open.
281  
        @throws std::logic_error if the socket is not open.
281  

282  

282  
        @par Preconditions
283  
        @par Preconditions
283  
        The socket must be open (`is_open() == true`).
284  
        The socket must be open (`is_open() == true`).
284  

285  

285  
        @par Example
286  
        @par Example
286  
        @code
287  
        @code
287  
        auto [ec] = co_await s.connect(endpoint);
288  
        auto [ec] = co_await s.connect(endpoint);
288  
        if (ec) { ... }
289  
        if (ec) { ... }
289  
        @endcode
290  
        @endcode
290  
    */
291  
    */
291  
    auto connect(endpoint ep)
292  
    auto connect(endpoint ep)
292  
    {
293  
    {
293  
        if (!is_open())
294  
        if (!is_open())
294  
            detail::throw_logic_error("connect: socket not open");
295  
            detail::throw_logic_error("connect: socket not open");
295  
        return connect_awaitable(*this, ep);
296  
        return connect_awaitable(*this, ep);
296  
    }
297  
    }
297  

298  

298  
    /** Cancel any pending asynchronous operations.
299  
    /** Cancel any pending asynchronous operations.
299  

300  

300  
        All outstanding operations complete with `errc::operation_canceled`.
301  
        All outstanding operations complete with `errc::operation_canceled`.
301  
        Check `ec == cond::canceled` for portable comparison.
302  
        Check `ec == cond::canceled` for portable comparison.
302  
    */
303  
    */
303  
    void cancel();
304  
    void cancel();
304  

305  

305  
    /** Get the native socket handle.
306  
    /** Get the native socket handle.
306  

307  

307  
        Returns the underlying platform-specific socket descriptor.
308  
        Returns the underlying platform-specific socket descriptor.
308  
        On POSIX systems this is an `int` file descriptor.
309  
        On POSIX systems this is an `int` file descriptor.
309  
        On Windows this is a `SOCKET` handle.
310  
        On Windows this is a `SOCKET` handle.
310  

311  

311  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
312  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
312  

313  

313  
        @par Preconditions
314  
        @par Preconditions
314  
        None. May be called on closed sockets.
315  
        None. May be called on closed sockets.
315  
    */
316  
    */
316  
    native_handle_type native_handle() const noexcept;
317  
    native_handle_type native_handle() const noexcept;
317  

318  

318  
    /** Disable sends or receives on the socket.
319  
    /** Disable sends or receives on the socket.
319  

320  

320  
        TCP connections are full-duplex: each direction (send and receive)
321  
        TCP connections are full-duplex: each direction (send and receive)
321  
        operates independently. This function allows you to close one or
322  
        operates independently. This function allows you to close one or
322  
        both directions without destroying the socket.
323  
        both directions without destroying the socket.
323  

324  

324  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
325  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
325  
            signaling that you have no more data to send. You can still
326  
            signaling that you have no more data to send. You can still
326  
            receive data until the peer also closes their send direction.
327  
            receive data until the peer also closes their send direction.
327  
            This is the most common use case, typically called before
328  
            This is the most common use case, typically called before
328  
            close() to ensure graceful connection termination.
329  
            close() to ensure graceful connection termination.
329  

330  

330  
        @li @ref shutdown_receive disables reading on the socket. This
331  
        @li @ref shutdown_receive disables reading on the socket. This
331  
            does NOT send anything to the peer - they are not informed
332  
            does NOT send anything to the peer - they are not informed
332  
            and may continue sending data. Subsequent reads will fail
333  
            and may continue sending data. Subsequent reads will fail
333  
            or return end-of-file. Incoming data may be discarded or
334  
            or return end-of-file. Incoming data may be discarded or
334  
            buffered depending on the operating system.
335  
            buffered depending on the operating system.
335  

336  

336  
        @li @ref shutdown_both combines both effects: sends a FIN and
337  
        @li @ref shutdown_both combines both effects: sends a FIN and
337  
            disables reading.
338  
            disables reading.
338  

339  

339  
        When the peer shuts down their send direction (sends a FIN),
340  
        When the peer shuts down their send direction (sends a FIN),
340  
        subsequent read operations will complete with `capy::cond::eof`.
341  
        subsequent read operations will complete with `capy::cond::eof`.
341  
        Use the portable condition test rather than comparing error
342  
        Use the portable condition test rather than comparing error
342  
        codes directly:
343  
        codes directly:
343  

344  

344  
        @code
345  
        @code
345  
        auto [ec, n] = co_await sock.read_some(buffer);
346  
        auto [ec, n] = co_await sock.read_some(buffer);
346  
        if (ec == capy::cond::eof)
347  
        if (ec == capy::cond::eof)
347  
        {
348  
        {
348  
            // Peer closed their send direction
349  
            // Peer closed their send direction
349  
        }
350  
        }
350  
        @endcode
351  
        @endcode
351  

352  

352  
        Any error from the underlying system call is silently discarded
353  
        Any error from the underlying system call is silently discarded
353  
        because it is unlikely to be helpful.
354  
        because it is unlikely to be helpful.
354  

355  

355  
        @param what Determines what operations will no longer be allowed.
356  
        @param what Determines what operations will no longer be allowed.
356  
    */
357  
    */
357  
    void shutdown(shutdown_type what);
358  
    void shutdown(shutdown_type what);
358  

359  

359  
    //
360  
    //
360  
    // Socket Options
361  
    // Socket Options
361  
    //
362  
    //
362  

363  

363  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
364  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
364  

365  

365  
        When enabled, segments are sent as soon as possible even if
366  
        When enabled, segments are sent as soon as possible even if
366  
        there is only a small amount of data. This reduces latency
367  
        there is only a small amount of data. This reduces latency
367  
        at the potential cost of increased network traffic.
368  
        at the potential cost of increased network traffic.
368  

369  

369  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
370  
        @param value `true` to disable Nagle's algorithm (enable no-delay).
370  

371  

371  
        @throws std::logic_error if the socket is not open.
372  
        @throws std::logic_error if the socket is not open.
372  
        @throws std::system_error on failure.
373  
        @throws std::system_error on failure.
373  
    */
374  
    */
374  
    void set_no_delay(bool value);
375  
    void set_no_delay(bool value);
375  

376  

376  
    /** Get the current TCP_NODELAY setting.
377  
    /** Get the current TCP_NODELAY setting.
377  

378  

378  
        @return `true` if Nagle's algorithm is disabled.
379  
        @return `true` if Nagle's algorithm is disabled.
379  

380  

380  
        @throws std::logic_error if the socket is not open.
381  
        @throws std::logic_error if the socket is not open.
381  
        @throws std::system_error on failure.
382  
        @throws std::system_error on failure.
382  
    */
383  
    */
383  
    bool no_delay() const;
384  
    bool no_delay() const;
384  

385  

385  
    /** Enable or disable SO_KEEPALIVE.
386  
    /** Enable or disable SO_KEEPALIVE.
386  

387  

387  
        When enabled, the socket will periodically send keepalive probes
388  
        When enabled, the socket will periodically send keepalive probes
388  
        to detect if the peer is still reachable.
389  
        to detect if the peer is still reachable.
389  

390  

390  
        @param value `true` to enable keepalive probes.
391  
        @param value `true` to enable keepalive probes.
391  

392  

392  
        @throws std::logic_error if the socket is not open.
393  
        @throws std::logic_error if the socket is not open.
393  
        @throws std::system_error on failure.
394  
        @throws std::system_error on failure.
394  
    */
395  
    */
395  
    void set_keep_alive(bool value);
396  
    void set_keep_alive(bool value);
396  

397  

397  
    /** Get the current SO_KEEPALIVE setting.
398  
    /** Get the current SO_KEEPALIVE setting.
398  

399  

399  
        @return `true` if keepalive is enabled.
400  
        @return `true` if keepalive is enabled.
400  

401  

401  
        @throws std::logic_error if the socket is not open.
402  
        @throws std::logic_error if the socket is not open.
402  
        @throws std::system_error on failure.
403  
        @throws std::system_error on failure.
403  
    */
404  
    */
404  
    bool keep_alive() const;
405  
    bool keep_alive() const;
405  

406  

406  
    /** Set the receive buffer size (SO_RCVBUF).
407  
    /** Set the receive buffer size (SO_RCVBUF).
407  

408  

408  
        @param size The desired receive buffer size in bytes.
409  
        @param size The desired receive buffer size in bytes.
409  

410  

410  
        @throws std::logic_error if the socket is not open.
411  
        @throws std::logic_error if the socket is not open.
411  
        @throws std::system_error on failure.
412  
        @throws std::system_error on failure.
412  

413  

413  
        @note The operating system may adjust the actual buffer size.
414  
        @note The operating system may adjust the actual buffer size.
414  
    */
415  
    */
415  
    void set_receive_buffer_size(int size);
416  
    void set_receive_buffer_size(int size);
416  

417  

417  
    /** Get the receive buffer size (SO_RCVBUF).
418  
    /** Get the receive buffer size (SO_RCVBUF).
418  

419  

419  
        @return The current receive buffer size in bytes.
420  
        @return The current receive buffer size in bytes.
420  

421  

421  
        @throws std::logic_error if the socket is not open.
422  
        @throws std::logic_error if the socket is not open.
422  
        @throws std::system_error on failure.
423  
        @throws std::system_error on failure.
423  
    */
424  
    */
424  
    int receive_buffer_size() const;
425  
    int receive_buffer_size() const;
425  

426  

426  
    /** Set the send buffer size (SO_SNDBUF).
427  
    /** Set the send buffer size (SO_SNDBUF).
427  

428  

428  
        @param size The desired send buffer size in bytes.
429  
        @param size The desired send buffer size in bytes.
429  

430  

430  
        @throws std::logic_error if the socket is not open.
431  
        @throws std::logic_error if the socket is not open.
431  
        @throws std::system_error on failure.
432  
        @throws std::system_error on failure.
432  

433  

433  
        @note The operating system may adjust the actual buffer size.
434  
        @note The operating system may adjust the actual buffer size.
434  
    */
435  
    */
435  
    void set_send_buffer_size(int size);
436  
    void set_send_buffer_size(int size);
436  

437  

437  
    /** Get the send buffer size (SO_SNDBUF).
438  
    /** Get the send buffer size (SO_SNDBUF).
438  

439  

439  
        @return The current send buffer size in bytes.
440  
        @return The current send buffer size in bytes.
440  

441  

441  
        @throws std::logic_error if the socket is not open.
442  
        @throws std::logic_error if the socket is not open.
442  
        @throws std::system_error on failure.
443  
        @throws std::system_error on failure.
443  
    */
444  
    */
444  
    int send_buffer_size() const;
445  
    int send_buffer_size() const;
445  

446  

446  
    /** Set the SO_LINGER option.
447  
    /** Set the SO_LINGER option.
447  

448  

448  
        Controls behavior when closing a socket with unsent data.
449  
        Controls behavior when closing a socket with unsent data.
449  

450  

450  
        @param enabled If `true`, close() will block until data is sent
451  
        @param enabled If `true`, close() will block until data is sent
451  
            or the timeout expires. If `false`, close() returns immediately.
452  
            or the timeout expires. If `false`, close() returns immediately.
452  
        @param timeout The linger timeout in seconds (only used if enabled).
453  
        @param timeout The linger timeout in seconds (only used if enabled).
453  

454  

454  
        @throws std::logic_error if the socket is not open.
455  
        @throws std::logic_error if the socket is not open.
455  
        @throws std::system_error on failure.
456  
        @throws std::system_error on failure.
456  
    */
457  
    */
457  
    void set_linger(bool enabled, int timeout);
458  
    void set_linger(bool enabled, int timeout);
458  

459  

459  
    /** Get the current SO_LINGER setting.
460  
    /** Get the current SO_LINGER setting.
460  

461  

461  
        @return The current linger options.
462  
        @return The current linger options.
462  

463  

463  
        @throws std::logic_error if the socket is not open.
464  
        @throws std::logic_error if the socket is not open.
464  
        @throws std::system_error on failure.
465  
        @throws std::system_error on failure.
465  
    */
466  
    */
466  
    linger_options linger() const;
467  
    linger_options linger() const;
467  

468  

468  
    /** Get the local endpoint of the socket.
469  
    /** Get the local endpoint of the socket.
469  

470  

470  
        Returns the local address and port to which the socket is bound.
471  
        Returns the local address and port to which the socket is bound.
471  
        For a connected socket, this is the local side of the connection.
472  
        For a connected socket, this is the local side of the connection.
472  
        The endpoint is cached when the connection is established.
473  
        The endpoint is cached when the connection is established.
473  

474  

474  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
475  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
475  
            the socket is not connected.
476  
            the socket is not connected.
476  

477  

477  
        @par Thread Safety
478  
        @par Thread Safety
478  
        The cached endpoint value is set during connect/accept completion
479  
        The cached endpoint value is set during connect/accept completion
479  
        and cleared during close(). This function may be called concurrently
480  
        and cleared during close(). This function may be called concurrently
480  
        with I/O operations, but must not be called concurrently with
481  
        with I/O operations, but must not be called concurrently with
481  
        connect(), accept(), or close().
482  
        connect(), accept(), or close().
482  
    */
483  
    */
483  
    endpoint local_endpoint() const noexcept;
484  
    endpoint local_endpoint() const noexcept;
484  

485  

485  
    /** Get the remote endpoint of the socket.
486  
    /** Get the remote endpoint of the socket.
486  

487  

487  
        Returns the remote address and port to which the socket is connected.
488  
        Returns the remote address and port to which the socket is connected.
488  
        The endpoint is cached when the connection is established.
489  
        The endpoint is cached when the connection is established.
489  

490  

490  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
491  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
491  
            the socket is not connected.
492  
            the socket is not connected.
492  

493  

493  
        @par Thread Safety
494  
        @par Thread Safety
494  
        The cached endpoint value is set during connect/accept completion
495  
        The cached endpoint value is set during connect/accept completion
495  
        and cleared during close(). This function may be called concurrently
496  
        and cleared during close(). This function may be called concurrently
496  
        with I/O operations, but must not be called concurrently with
497  
        with I/O operations, but must not be called concurrently with
497  
        connect(), accept(), or close().
498  
        connect(), accept(), or close().
498  
    */
499  
    */
499  
    endpoint remote_endpoint() const noexcept;
500  
    endpoint remote_endpoint() const noexcept;
 
501 +

 
502 +
protected:
 
503 +
    tcp_socket() noexcept = default;
 
504 +

 
505 +
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
500  

506  

501  
private:
507  
private:
502  
    friend class tcp_acceptor;
508  
    friend class tcp_acceptor;
503  

509  

504  
    inline implementation& get() const noexcept
510  
    inline implementation& get() const noexcept
505  
    {
511  
    {
506  
        return *static_cast<implementation*>(h_.get());
512  
        return *static_cast<implementation*>(h_.get());
507  
    }
513  
    }
508  
};
514  
};
509  

515  

510  
} // namespace boost::corosio
516  
} // namespace boost::corosio
511  

517  

512  
#endif
518  
#endif