1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.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/ex/io_env.hpp>
22  
#include <boost/capy/ex/io_env.hpp>
23  
#include <boost/capy/concept/executor.hpp>
23  
#include <boost/capy/concept/executor.hpp>
24  

24  

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

26  

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

33  

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

35  

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

41  

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

43  

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

47  

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

51  

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

57  

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

62  

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

68  

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

74  

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

90  

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

97  

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

106  

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

108  

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

110  

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

112  

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

117  

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

121  

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

124  

125  
        virtual std::error_code set_receive_buffer_size(int size) noexcept = 0;
125  
        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;
126  
        virtual int receive_buffer_size(std::error_code& ec) const noexcept = 0;
127  

127  

128  
        virtual std::error_code set_send_buffer_size(int size) noexcept = 0;
128  
        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;
129  
        virtual int send_buffer_size(std::error_code& ec) const noexcept = 0;
130  

130  

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

134  

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

137  

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

141  

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

148  

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

154  

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

159  

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

166  

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

174  

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

177  

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

181  

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

183  

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

187  

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

189  

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

191  

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

200  

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

202  

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

204  

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

208  

213  
    /** Move assignment operator.
209  
    /** Move assignment operator.
214  

210  

215 -
        The source and destination must share the same execution context.
 
216 -

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

213  

220 -

 
221 -
        @throws std::logic_error if the sockets have different execution contexts.
 
222  
        @return Reference to this socket.
214  
        @return Reference to this socket.
223  
    */
215  
    */
224 -
    tcp_socket& operator=(tcp_socket&& other)
216 +
    tcp_socket& operator=(tcp_socket&& other) noexcept
225  
    {
217  
    {
226  
        if (this != &other)
218  
        if (this != &other)
227 -
            if (&context() != &other.context())
 
228 -
                detail::throw_logic_error(
 
229 -
                    "cannot move socket across execution contexts");
 
230  
        {
219  
        {
231  
            close();
220  
            close();
232  
            h_ = std::move(other.h_);
221  
            h_ = std::move(other.h_);
233  
        }
222  
        }
234  
        return *this;
223  
        return *this;
235  
    }
224  
    }
236  

225  

237  
    tcp_socket(tcp_socket const&) = delete;
226  
    tcp_socket(tcp_socket const&) = delete;
238  
    tcp_socket& operator=(tcp_socket const&) = delete;
227  
    tcp_socket& operator=(tcp_socket const&) = delete;
239  

228  

240  
    /** Open the socket.
229  
    /** Open the socket.
241  

230  

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

234  

246  
        @throws std::system_error on failure.
235  
        @throws std::system_error on failure.
247  
    */
236  
    */
248  
    void open();
237  
    void open();
249  

238  

250  
    /** Close the socket.
239  
    /** Close the socket.
251  

240  

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

245  

257  
    /** Check if the socket is open.
246  
    /** Check if the socket is open.
258  

247  

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

258  

270  
    /** Initiate an asynchronous connect operation.
259  
    /** Initiate an asynchronous connect operation.
271  

260  

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

263  

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

268  

280  
        @param ep The remote endpoint to connect to.
269  
        @param ep The remote endpoint to connect to.
281  

270  

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

279  

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

281  

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

284  

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

297  

309  
    /** Cancel any pending asynchronous operations.
298  
    /** Cancel any pending asynchronous operations.
310  

299  

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

304  

316  
    /** Get the native socket handle.
305  
    /** Get the native socket handle.
317  

306  

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

310  

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

312  

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

317  

329  
    /** Disable sends or receives on the socket.
318  
    /** Disable sends or receives on the socket.
330  

319  

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

323  

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

329  

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

335  

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

338  

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

343  

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

351  

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

354  

366  
        @param what Determines what operations will no longer be allowed.
355  
        @param what Determines what operations will no longer be allowed.
367  
    */
356  
    */
368  
    void shutdown(shutdown_type what);
357  
    void shutdown(shutdown_type what);
369 -
    //--------------------------------------------------------------------------
 
370  

358  

371  
    //
359  
    //
372  
    // Socket Options
360  
    // Socket Options
373 -
    //--------------------------------------------------------------------------
 
374  
    //
361  
    //
375  

362  

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

364  

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

368  

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

370  

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

375  

389  
    /** Get the current TCP_NODELAY setting.
376  
    /** Get the current TCP_NODELAY setting.
390  

377  

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

379  

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

384  

398  
    /** Enable or disable SO_KEEPALIVE.
385  
    /** Enable or disable SO_KEEPALIVE.
399  

386  

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

389  

403  
        @param value `true` to enable keepalive probes.
390  
        @param value `true` to enable keepalive probes.
404  

391  

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

396  

410  
    /** Get the current SO_KEEPALIVE setting.
397  
    /** Get the current SO_KEEPALIVE setting.
411  

398  

412  
        @return `true` if keepalive is enabled.
399  
        @return `true` if keepalive is enabled.
413  

400  

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

405  

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

407  

421  
        @param size The desired receive buffer size in bytes.
408  
        @param size The desired receive buffer size in bytes.
422  

409  

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

412  

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

416  

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

418  

432  
        @return The current receive buffer size in bytes.
419  
        @return The current receive buffer size in bytes.
433  

420  

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

425  

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

427  

441  
        @param size The desired send buffer size in bytes.
428  
        @param size The desired send buffer size in bytes.
442  

429  

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

432  

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

436  

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

438  

452  
        @return The current send buffer size in bytes.
439  
        @return The current send buffer size in bytes.
453  

440  

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

445  

459  
    /** Set the SO_LINGER option.
446  
    /** Set the SO_LINGER option.
460  

447  

461  
        Controls behavior when closing a socket with unsent data.
448  
        Controls behavior when closing a socket with unsent data.
462  

449  

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

453  

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

458  

472  
    /** Get the current SO_LINGER setting.
459  
    /** Get the current SO_LINGER setting.
473  

460  

474  
        @return The current linger options.
461  
        @return The current linger options.
475  

462  

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

467  

481  
    /** Get the local endpoint of the socket.
468  
    /** Get the local endpoint of the socket.
482  

469  

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

473  

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

476  

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

484  

498  
    /** Get the remote endpoint of the socket.
485  
    /** Get the remote endpoint of the socket.
499  

486  

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

489  

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

492  

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

500  

514  
private:
501  
private:
515  
    friend class tcp_acceptor;
502  
    friend class tcp_acceptor;
516  

503  

517  
    inline implementation& get() const noexcept
504  
    inline implementation& get() const noexcept
518  
    {
505  
    {
519  
        return *static_cast<implementation*>(h_.get());
506  
        return *static_cast<implementation*>(h_.get());
520  
    }
507  
    }
521  
};
508  
};
522  

509  

523  
} // namespace boost::corosio
510  
} // namespace boost::corosio
524  

511  

525  
#endif
512  
#endif