1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie dot falco at gmail dot com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
4  
// 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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/cppalliance/corosio
7  
// Official repository: https://github.com/cppalliance/corosio
8  
//
8  
//
9  

9  

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

12  

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

23  

24  
#include <system_error>
24  
#include <system_error>
25  

25  

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

32  

33  
namespace boost::corosio {
33  
namespace boost::corosio {
34  

34  

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

40  

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

42  

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

46  

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

50  

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

56  

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

61  

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

67  

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

73  

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

89  

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

96  

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

105  

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

107  

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

109  

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

111  

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

116  

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

120  

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

123  

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

126  

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

129  

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

132  

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

135  

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

139  

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

146  

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

152  

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

157  

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

164  

165  
        template<typename Ex>
165  
        template<typename Ex>
166  
        auto await_suspend(
166  
        auto await_suspend(
167  
            std::coroutine_handle<> h,
167  
            std::coroutine_handle<> h,
168  
            Ex const& ex) -> std::coroutine_handle<>
168  
            Ex const& ex) -> std::coroutine_handle<>
169  
        {
169  
        {
170  
            s_.get().connect(h, ex, endpoint_, token_, &ec_);
170  
            s_.get().connect(h, ex, endpoint_, token_, &ec_);
171  
            return std::noop_coroutine();
171  
            return std::noop_coroutine();
172  
        }
172  
        }
173  

173  

174  
        template<typename Ex>
174  
        template<typename Ex>
175  
        auto await_suspend(
175  
        auto await_suspend(
176  
            std::coroutine_handle<> h,
176  
            std::coroutine_handle<> h,
177  
            Ex const& ex,
177  
            Ex const& ex,
178  
            std::stop_token token) -> std::coroutine_handle<>
178  
            std::stop_token token) -> std::coroutine_handle<>
179  
        {
179  
        {
180  
            token_ = std::move(token);
180  
            token_ = std::move(token);
181  
            s_.get().connect(h, ex, endpoint_, token_, &ec_);
181  
            s_.get().connect(h, ex, endpoint_, token_, &ec_);
182  
            return std::noop_coroutine();
182  
            return std::noop_coroutine();
183  
        }
183  
        }
184  
    };
184  
    };
185  

185  

186  
public:
186  
public:
187  
    /** Destructor.
187  
    /** Destructor.
188  

188  

189  
        Closes the socket if open, cancelling any pending operations.
189  
        Closes the socket if open, cancelling any pending operations.
190  
    */
190  
    */
191  
    ~tcp_socket();
191  
    ~tcp_socket();
192  

192  

193  
    /** Construct a socket from an execution context.
193  
    /** Construct a socket from an execution context.
194  

194  

195  
        @param ctx The execution context that will own this socket.
195  
        @param ctx The execution context that will own this socket.
196  
    */
196  
    */
197  
    explicit tcp_socket(capy::execution_context& ctx);
197  
    explicit tcp_socket(capy::execution_context& ctx);
198  

198  

199  
    /** Construct a socket from an executor.
199  
    /** Construct a socket from an executor.
200  

200  

201  
        The socket is associated with the executor's context.
201  
        The socket is associated with the executor's context.
202  

202  

203  
        @param ex The executor whose context will own the socket.
203  
        @param ex The executor whose context will own the socket.
204  
    */
204  
    */
205  
    template<class Ex>
205  
    template<class Ex>
206  
        requires (!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
206  
        requires (!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
207  
                 capy::Executor<Ex>
207  
                 capy::Executor<Ex>
208  
    explicit tcp_socket(Ex const& ex)
208  
    explicit tcp_socket(Ex const& ex)
209  
        : tcp_socket(ex.context())
209  
        : tcp_socket(ex.context())
210  
    {
210  
    {
211  
    }
211  
    }
212  

212  

213  
    /** Move constructor.
213  
    /** Move constructor.
214  

214  

215  
        Transfers ownership of the socket resources.
215  
        Transfers ownership of the socket resources.
216  

216  

217  
        @param other The socket to move from.
217  
        @param other The socket to move from.
218  
    */
218  
    */
219  
    tcp_socket(tcp_socket&& other) noexcept
219  
    tcp_socket(tcp_socket&& other) noexcept
220  
        : io_stream(other.context())
220  
        : io_stream(other.context())
221  
    {
221  
    {
222  
        impl_ = other.impl_;
222  
        impl_ = other.impl_;
223  
        other.impl_ = nullptr;
223  
        other.impl_ = nullptr;
224  
    }
224  
    }
225  

225  

226  
    /** Move assignment operator.
226  
    /** Move assignment operator.
227  

227  

228  
        Closes any existing socket and transfers ownership.
228  
        Closes any existing socket and transfers ownership.
229  
        The source and destination must share the same execution context.
229  
        The source and destination must share the same execution context.
230  

230  

231  
        @param other The socket to move from.
231  
        @param other The socket to move from.
232  

232  

233  
        @return Reference to this socket.
233  
        @return Reference to this socket.
234  

234  

235  
        @throws std::logic_error if the sockets have different execution contexts.
235  
        @throws std::logic_error if the sockets have different execution contexts.
236  
    */
236  
    */
237  
    tcp_socket& operator=(tcp_socket&& other)
237  
    tcp_socket& operator=(tcp_socket&& other)
238  
    {
238  
    {
239  
        if (this != &other)
239  
        if (this != &other)
240  
        {
240  
        {
241  
            if (ctx_ != other.ctx_)
241  
            if (ctx_ != other.ctx_)
242  
                detail::throw_logic_error(
242  
                detail::throw_logic_error(
243  
                    "cannot move socket across execution contexts");
243  
                    "cannot move socket across execution contexts");
244  
            close();
244  
            close();
245  
            impl_ = other.impl_;
245  
            impl_ = other.impl_;
246  
            other.impl_ = nullptr;
246  
            other.impl_ = nullptr;
247  
        }
247  
        }
248  
        return *this;
248  
        return *this;
249  
    }
249  
    }
250  

250  

251  
    tcp_socket(tcp_socket const&) = delete;
251  
    tcp_socket(tcp_socket const&) = delete;
252  
    tcp_socket& operator=(tcp_socket const&) = delete;
252  
    tcp_socket& operator=(tcp_socket const&) = delete;
253  

253  

254  
    /** Open the socket.
254  
    /** Open the socket.
255  

255  

256  
        Creates an IPv4 TCP socket and associates it with the platform
256  
        Creates an IPv4 TCP socket and associates it with the platform
257  
        reactor (IOCP on Windows). This must be called before initiating
257  
        reactor (IOCP on Windows). This must be called before initiating
258  
        I/O operations.
258  
        I/O operations.
259  

259  

260  
        @throws std::system_error on failure.
260  
        @throws std::system_error on failure.
261  
    */
261  
    */
262  
    void open();
262  
    void open();
263  

263  

264  
    /** Close the socket.
264  
    /** Close the socket.
265  

265  

266  
        Releases socket resources. Any pending operations complete
266  
        Releases socket resources. Any pending operations complete
267  
        with `errc::operation_canceled`.
267  
        with `errc::operation_canceled`.
268  
    */
268  
    */
269  
    void close();
269  
    void close();
270  

270  

271  
    /** Check if the socket is open.
271  
    /** Check if the socket is open.
272  

272  

273  
        @return `true` if the socket is open and ready for operations.
273  
        @return `true` if the socket is open and ready for operations.
274  
    */
274  
    */
275  
    bool is_open() const noexcept
275  
    bool is_open() const noexcept
276  
    {
276  
    {
277  
        return impl_ != nullptr;
277  
        return impl_ != nullptr;
278  
    }
278  
    }
279  

279  

280  
    /** Initiate an asynchronous connect operation.
280  
    /** Initiate an asynchronous connect operation.
281  

281  

282  
        Connects the socket to the specified remote endpoint. The socket
282  
        Connects the socket to the specified remote endpoint. The socket
283  
        must be open before calling this function.
283  
        must be open before calling this function.
284  

284  

285  
        The operation supports cancellation via `std::stop_token` through
285  
        The operation supports cancellation via `std::stop_token` through
286  
        the affine awaitable protocol. If the associated stop token is
286  
        the affine awaitable protocol. If the associated stop token is
287  
        triggered, the operation completes immediately with
287  
        triggered, the operation completes immediately with
288  
        `errc::operation_canceled`.
288  
        `errc::operation_canceled`.
289  

289  

290  
        @param ep The remote endpoint to connect to.
290  
        @param ep The remote endpoint to connect to.
291  

291  

292  
        @return An awaitable that completes with `io_result<>`.
292  
        @return An awaitable that completes with `io_result<>`.
293  
            Returns success (default error_code) on successful connection,
293  
            Returns success (default error_code) on successful connection,
294  
            or an error code on failure including:
294  
            or an error code on failure including:
295  
            - connection_refused: No server listening at endpoint
295  
            - connection_refused: No server listening at endpoint
296  
            - timed_out: Connection attempt timed out
296  
            - timed_out: Connection attempt timed out
297  
            - network_unreachable: No route to host
297  
            - network_unreachable: No route to host
298  
            - operation_canceled: Cancelled via stop_token or cancel().
298  
            - operation_canceled: Cancelled via stop_token or cancel().
299  
                Check `ec == cond::canceled` for portable comparison.
299  
                Check `ec == cond::canceled` for portable comparison.
300  

300  

301  
        @throws std::logic_error if the socket is not open.
301  
        @throws std::logic_error if the socket is not open.
302  

302  

303  
        @par Preconditions
303  
        @par Preconditions
304  
        The socket must be open (`is_open() == true`).
304  
        The socket must be open (`is_open() == true`).
305  

305  

306  
        @par Example
306  
        @par Example
307  
        @code
307  
        @code
308  
        auto [ec] = co_await s.connect(endpoint);
308  
        auto [ec] = co_await s.connect(endpoint);
309  
        if (ec) { ... }
309  
        if (ec) { ... }
310  
        @endcode
310  
        @endcode
311  
    */
311  
    */
312  
    auto connect(endpoint ep)
312  
    auto connect(endpoint ep)
313  
    {
313  
    {
314  
        if (!impl_)
314  
        if (!impl_)
315  
            detail::throw_logic_error("connect: socket not open");
315  
            detail::throw_logic_error("connect: socket not open");
316  
        return connect_awaitable(*this, ep);
316  
        return connect_awaitable(*this, ep);
317  
    }
317  
    }
318  

318  

319  
    /** Cancel any pending asynchronous operations.
319  
    /** Cancel any pending asynchronous operations.
320  

320  

321  
        All outstanding operations complete with `errc::operation_canceled`.
321  
        All outstanding operations complete with `errc::operation_canceled`.
322  
        Check `ec == cond::canceled` for portable comparison.
322  
        Check `ec == cond::canceled` for portable comparison.
323  
    */
323  
    */
324  
    void cancel();
324  
    void cancel();
325  

325  

326  
    /** Get the native socket handle.
326  
    /** Get the native socket handle.
327  

327  

328  
        Returns the underlying platform-specific socket descriptor.
328  
        Returns the underlying platform-specific socket descriptor.
329  
        On POSIX systems this is an `int` file descriptor.
329  
        On POSIX systems this is an `int` file descriptor.
330  
        On Windows this is a `SOCKET` handle.
330  
        On Windows this is a `SOCKET` handle.
331  

331  

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

333  

334  
        @par Preconditions
334  
        @par Preconditions
335  
        None. May be called on closed sockets.
335  
        None. May be called on closed sockets.
336  
    */
336  
    */
337  
    native_handle_type native_handle() const noexcept;
337  
    native_handle_type native_handle() const noexcept;
338  

338  

339  
    /** Disable sends or receives on the socket.
339  
    /** Disable sends or receives on the socket.
340  

340  

341  
        TCP connections are full-duplex: each direction (send and receive)
341  
        TCP connections are full-duplex: each direction (send and receive)
342  
        operates independently. This function allows you to close one or
342  
        operates independently. This function allows you to close one or
343  
        both directions without destroying the socket.
343  
        both directions without destroying the socket.
344  

344  

345  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
345  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
346  
            signaling that you have no more data to send. You can still
346  
            signaling that you have no more data to send. You can still
347  
            receive data until the peer also closes their send direction.
347  
            receive data until the peer also closes their send direction.
348  
            This is the most common use case, typically called before
348  
            This is the most common use case, typically called before
349  
            close() to ensure graceful connection termination.
349  
            close() to ensure graceful connection termination.
350  

350  

351  
        @li @ref shutdown_receive disables reading on the socket. This
351  
        @li @ref shutdown_receive disables reading on the socket. This
352  
            does NOT send anything to the peer - they are not informed
352  
            does NOT send anything to the peer - they are not informed
353  
            and may continue sending data. Subsequent reads will fail
353  
            and may continue sending data. Subsequent reads will fail
354  
            or return end-of-file. Incoming data may be discarded or
354  
            or return end-of-file. Incoming data may be discarded or
355  
            buffered depending on the operating system.
355  
            buffered depending on the operating system.
356  

356  

357  
        @li @ref shutdown_both combines both effects: sends a FIN and
357  
        @li @ref shutdown_both combines both effects: sends a FIN and
358  
            disables reading.
358  
            disables reading.
359  

359  

360  
        When the peer shuts down their send direction (sends a FIN),
360  
        When the peer shuts down their send direction (sends a FIN),
361  
        subsequent read operations will complete with `capy::cond::eof`.
361  
        subsequent read operations will complete with `capy::cond::eof`.
362  
        Use the portable condition test rather than comparing error
362  
        Use the portable condition test rather than comparing error
363  
        codes directly:
363  
        codes directly:
364  

364  

365  
        @code
365  
        @code
366  
        auto [ec, n] = co_await sock.read_some(buffer);
366  
        auto [ec, n] = co_await sock.read_some(buffer);
367  
        if (ec == capy::cond::eof)
367  
        if (ec == capy::cond::eof)
368  
        {
368  
        {
369  
            // Peer closed their send direction
369  
            // Peer closed their send direction
370  
        }
370  
        }
371  
        @endcode
371  
        @endcode
372  

372  

373  
        Any error from the underlying system call is silently discarded
373  
        Any error from the underlying system call is silently discarded
374  
        because it is unlikely to be helpful.
374  
        because it is unlikely to be helpful.
375  

375  

376  
        @param what Determines what operations will no longer be allowed.
376  
        @param what Determines what operations will no longer be allowed.
377  
    */
377  
    */
378  
    void shutdown(shutdown_type what);
378  
    void shutdown(shutdown_type what);
379  

379  

380  
    //--------------------------------------------------------------------------
380  
    //--------------------------------------------------------------------------
381  
    //
381  
    //
382  
    // Socket Options
382  
    // Socket Options
383  
    //
383  
    //
384  
    //--------------------------------------------------------------------------
384  
    //--------------------------------------------------------------------------
385  

385  

386  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
386  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
387  

387  

388  
        When enabled, segments are sent as soon as possible even if
388  
        When enabled, segments are sent as soon as possible even if
389  
        there is only a small amount of data. This reduces latency
389  
        there is only a small amount of data. This reduces latency
390  
        at the potential cost of increased network traffic.
390  
        at the potential cost of increased network traffic.
391  

391  

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

393  

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

398  

399  
    /** Get the current TCP_NODELAY setting.
399  
    /** Get the current TCP_NODELAY setting.
400  

400  

401  
        @return `true` if Nagle's algorithm is disabled.
401  
        @return `true` if Nagle's algorithm is disabled.
402  

402  

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

407  

408  
    /** Enable or disable SO_KEEPALIVE.
408  
    /** Enable or disable SO_KEEPALIVE.
409  

409  

410  
        When enabled, the socket will periodically send keepalive probes
410  
        When enabled, the socket will periodically send keepalive probes
411  
        to detect if the peer is still reachable.
411  
        to detect if the peer is still reachable.
412  

412  

413  
        @param value `true` to enable keepalive probes.
413  
        @param value `true` to enable keepalive probes.
414  

414  

415  
        @throws std::logic_error if the socket is not open.
415  
        @throws std::logic_error if the socket is not open.
416  
        @throws std::system_error on failure.
416  
        @throws std::system_error on failure.
417  
    */
417  
    */
418  
    void set_keep_alive(bool value);
418  
    void set_keep_alive(bool value);
419  

419  

420  
    /** Get the current SO_KEEPALIVE setting.
420  
    /** Get the current SO_KEEPALIVE setting.
421  

421  

422  
        @return `true` if keepalive is enabled.
422  
        @return `true` if keepalive is enabled.
423  

423  

424  
        @throws std::logic_error if the socket is not open.
424  
        @throws std::logic_error if the socket is not open.
425  
        @throws std::system_error on failure.
425  
        @throws std::system_error on failure.
426  
    */
426  
    */
427  
    bool keep_alive() const;
427  
    bool keep_alive() const;
428  

428  

429  
    /** Set the receive buffer size (SO_RCVBUF).
429  
    /** Set the receive buffer size (SO_RCVBUF).
430  

430  

431  
        @param size The desired receive buffer size in bytes.
431  
        @param size The desired receive buffer size in bytes.
432  

432  

433  
        @throws std::logic_error if the socket is not open.
433  
        @throws std::logic_error if the socket is not open.
434  
        @throws std::system_error on failure.
434  
        @throws std::system_error on failure.
435  

435  

436  
        @note The operating system may adjust the actual buffer size.
436  
        @note The operating system may adjust the actual buffer size.
437  
    */
437  
    */
438  
    void set_receive_buffer_size(int size);
438  
    void set_receive_buffer_size(int size);
439  

439  

440  
    /** Get the receive buffer size (SO_RCVBUF).
440  
    /** Get the receive buffer size (SO_RCVBUF).
441  

441  

442  
        @return The current receive buffer size in bytes.
442  
        @return The current receive buffer size in bytes.
443  

443  

444  
        @throws std::logic_error if the socket is not open.
444  
        @throws std::logic_error if the socket is not open.
445  
        @throws std::system_error on failure.
445  
        @throws std::system_error on failure.
446  
    */
446  
    */
447  
    int receive_buffer_size() const;
447  
    int receive_buffer_size() const;
448  

448  

449  
    /** Set the send buffer size (SO_SNDBUF).
449  
    /** Set the send buffer size (SO_SNDBUF).
450  

450  

451  
        @param size The desired send buffer size in bytes.
451  
        @param size The desired send buffer size in bytes.
452  

452  

453  
        @throws std::logic_error if the socket is not open.
453  
        @throws std::logic_error if the socket is not open.
454  
        @throws std::system_error on failure.
454  
        @throws std::system_error on failure.
455  

455  

456  
        @note The operating system may adjust the actual buffer size.
456  
        @note The operating system may adjust the actual buffer size.
457  
    */
457  
    */
458  
    void set_send_buffer_size(int size);
458  
    void set_send_buffer_size(int size);
459  

459  

460  
    /** Get the send buffer size (SO_SNDBUF).
460  
    /** Get the send buffer size (SO_SNDBUF).
461  

461  

462  
        @return The current send buffer size in bytes.
462  
        @return The current send buffer size in bytes.
463  

463  

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

468  

469  
    /** Set the SO_LINGER option.
469  
    /** Set the SO_LINGER option.
470  

470  

471  
        Controls behavior when closing a socket with unsent data.
471  
        Controls behavior when closing a socket with unsent data.
472  

472  

473  
        @param enabled If `true`, close() will block until data is sent
473  
        @param enabled If `true`, close() will block until data is sent
474  
            or the timeout expires. If `false`, close() returns immediately.
474  
            or the timeout expires. If `false`, close() returns immediately.
475  
        @param timeout The linger timeout in seconds (only used if enabled).
475  
        @param timeout The linger timeout in seconds (only used if enabled).
476  

476  

477  
        @throws std::logic_error if the socket is not open.
477  
        @throws std::logic_error if the socket is not open.
478  
        @throws std::system_error on failure.
478  
        @throws std::system_error on failure.
479  
    */
479  
    */
480  
    void set_linger(bool enabled, int timeout);
480  
    void set_linger(bool enabled, int timeout);
481  

481  

482  
    /** Get the current SO_LINGER setting.
482  
    /** Get the current SO_LINGER setting.
483  

483  

484  
        @return The current linger options.
484  
        @return The current linger options.
485  

485  

486  
        @throws std::logic_error if the socket is not open.
486  
        @throws std::logic_error if the socket is not open.
487  
        @throws std::system_error on failure.
487  
        @throws std::system_error on failure.
488  
    */
488  
    */
489  
    linger_options linger() const;
489  
    linger_options linger() const;
490  

490  

491  
    /** Get the local endpoint of the socket.
491  
    /** Get the local endpoint of the socket.
492  

492  

493  
        Returns the local address and port to which the socket is bound.
493  
        Returns the local address and port to which the socket is bound.
494  
        For a connected socket, this is the local side of the connection.
494  
        For a connected socket, this is the local side of the connection.
495  
        The endpoint is cached when the connection is established.
495  
        The endpoint is cached when the connection is established.
496  

496  

497  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
497  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
498  
            the socket is not connected.
498  
            the socket is not connected.
499  

499  

500  
        @par Thread Safety
500  
        @par Thread Safety
501  
        The cached endpoint value is set during connect/accept completion
501  
        The cached endpoint value is set during connect/accept completion
502  
        and cleared during close(). This function may be called concurrently
502  
        and cleared during close(). This function may be called concurrently
503  
        with I/O operations, but must not be called concurrently with
503  
        with I/O operations, but must not be called concurrently with
504  
        connect(), accept(), or close().
504  
        connect(), accept(), or close().
505  
    */
505  
    */
506  
    endpoint local_endpoint() const noexcept;
506  
    endpoint local_endpoint() const noexcept;
507  

507  

508  
    /** Get the remote endpoint of the socket.
508  
    /** Get the remote endpoint of the socket.
509  

509  

510  
        Returns the remote address and port to which the socket is connected.
510  
        Returns the remote address and port to which the socket is connected.
511  
        The endpoint is cached when the connection is established.
511  
        The endpoint is cached when the connection is established.
512  

512  

513  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
513  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
514  
            the socket is not connected.
514  
            the socket is not connected.
515  

515  

516  
        @par Thread Safety
516  
        @par Thread Safety
517  
        The cached endpoint value is set during connect/accept completion
517  
        The cached endpoint value is set during connect/accept completion
518  
        and cleared during close(). This function may be called concurrently
518  
        and cleared during close(). This function may be called concurrently
519  
        with I/O operations, but must not be called concurrently with
519  
        with I/O operations, but must not be called concurrently with
520  
        connect(), accept(), or close().
520  
        connect(), accept(), or close().
521  
    */
521  
    */
522  
    endpoint remote_endpoint() const noexcept;
522  
    endpoint remote_endpoint() const noexcept;
523  

523  

524  
private:
524  
private:
525  
    friend class tcp_acceptor;
525  
    friend class tcp_acceptor;
526  

526  

527  
    inline socket_impl& get() const noexcept
527  
    inline socket_impl& get() const noexcept
528  
    {
528  
    {
529  
        return *static_cast<socket_impl*>(impl_);
529  
        return *static_cast<socket_impl*>(impl_);
530  
    }
530  
    }
531  
};
531  
};
532  

532  

533  
} // namespace boost::corosio
533  
} // namespace boost::corosio
534  

534  

535  
#endif
535  
#endif