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 std::coroutine_handle<> connect(
99  
        virtual std::coroutine_handle<> 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  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
170  
            return s_.get().connect(h, ex, endpoint_, token_, &ec_);
171  
        }
171  
        }
172  

172  

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

183  

184  
public:
184  
public:
185  
    /** Destructor.
185  
    /** Destructor.
186  

186  

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

190  

191  
    /** Construct a socket from an execution context.
191  
    /** Construct a socket from an execution context.
192  

192  

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

196  

197  
    /** Construct a socket from an executor.
197  
    /** Construct a socket from an executor.
198  

198  

199  
        The socket is associated with the executor's context.
199  
        The socket is associated with the executor's context.
200  

200  

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

210  

211  
    /** Move constructor.
211  
    /** Move constructor.
212  

212  

213  
        Transfers ownership of the socket resources.
213  
        Transfers ownership of the socket resources.
214  

214  

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

223  

224  
    /** Move assignment operator.
224  
    /** Move assignment operator.
225  

225  

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

228  

229  
        @param other The socket to move from.
229  
        @param other The socket to move from.
230  

230  

231  
        @return Reference to this socket.
231  
        @return Reference to this socket.
232  

232  

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

248  

249  
    tcp_socket(tcp_socket const&) = delete;
249  
    tcp_socket(tcp_socket const&) = delete;
250  
    tcp_socket& operator=(tcp_socket const&) = delete;
250  
    tcp_socket& operator=(tcp_socket const&) = delete;
251  

251  

252  
    /** Open the socket.
252  
    /** Open the socket.
253  

253  

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

257  

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

261  

262  
    /** Close the socket.
262  
    /** Close the socket.
263  

263  

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

268  

269  
    /** Check if the socket is open.
269  
    /** Check if the socket is open.
270  

270  

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

277  

278  
    /** Initiate an asynchronous connect operation.
278  
    /** Initiate an asynchronous connect operation.
279  

279  

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

282  

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

287  

288  
        @param ep The remote endpoint to connect to.
288  
        @param ep The remote endpoint to connect to.
289  

289  

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

298  

299  
        @throws std::logic_error if the socket is not open.
299  
        @throws std::logic_error if the socket is not open.
300  

300  

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

303  

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

316  

317  
    /** Cancel any pending asynchronous operations.
317  
    /** Cancel any pending asynchronous operations.
318  

318  

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

323  

324  
    /** Get the native socket handle.
324  
    /** Get the native socket handle.
325  

325  

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

329  

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

331  

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

336  

337  
    /** Disable sends or receives on the socket.
337  
    /** Disable sends or receives on the socket.
338  

338  

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

342  

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

348  

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

354  

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

357  

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

362  

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

370  

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

373  

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

377  

378  
    //--------------------------------------------------------------------------
378  
    //--------------------------------------------------------------------------
379  
    //
379  
    //
380  
    // Socket Options
380  
    // Socket Options
381  
    //
381  
    //
382  
    //--------------------------------------------------------------------------
382  
    //--------------------------------------------------------------------------
383  

383  

384  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
384  
    /** Enable or disable TCP_NODELAY (disable Nagle's algorithm).
385  

385  

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

389  

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

391  

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

396  

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

398  

399  
        @return `true` if Nagle's algorithm is disabled.
399  
        @return `true` if Nagle's algorithm is disabled.
400  

400  

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

405  

406  
    /** Enable or disable SO_KEEPALIVE.
406  
    /** Enable or disable SO_KEEPALIVE.
407  

407  

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

410  

411  
        @param value `true` to enable keepalive probes.
411  
        @param value `true` to enable keepalive probes.
412  

412  

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

417  

418  
    /** Get the current SO_KEEPALIVE setting.
418  
    /** Get the current SO_KEEPALIVE setting.
419  

419  

420  
        @return `true` if keepalive is enabled.
420  
        @return `true` if keepalive is enabled.
421  

421  

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

426  

427  
    /** Set the receive buffer size (SO_RCVBUF).
427  
    /** Set the receive buffer size (SO_RCVBUF).
428  

428  

429  
        @param size The desired receive buffer size in bytes.
429  
        @param size The desired receive buffer size in bytes.
430  

430  

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

433  

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

437  

438  
    /** Get the receive buffer size (SO_RCVBUF).
438  
    /** Get the receive buffer size (SO_RCVBUF).
439  

439  

440  
        @return The current receive buffer size in bytes.
440  
        @return The current receive buffer size in bytes.
441  

441  

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

446  

447  
    /** Set the send buffer size (SO_SNDBUF).
447  
    /** Set the send buffer size (SO_SNDBUF).
448  

448  

449  
        @param size The desired send buffer size in bytes.
449  
        @param size The desired send buffer size in bytes.
450  

450  

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

453  

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

457  

458  
    /** Get the send buffer size (SO_SNDBUF).
458  
    /** Get the send buffer size (SO_SNDBUF).
459  

459  

460  
        @return The current send buffer size in bytes.
460  
        @return The current send buffer size in bytes.
461  

461  

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

466  

467  
    /** Set the SO_LINGER option.
467  
    /** Set the SO_LINGER option.
468  

468  

469  
        Controls behavior when closing a socket with unsent data.
469  
        Controls behavior when closing a socket with unsent data.
470  

470  

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

474  

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

479  

480  
    /** Get the current SO_LINGER setting.
480  
    /** Get the current SO_LINGER setting.
481  

481  

482  
        @return The current linger options.
482  
        @return The current linger options.
483  

483  

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

488  

489  
    /** Get the local endpoint of the socket.
489  
    /** Get the local endpoint of the socket.
490  

490  

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

494  

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

497  

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

505  

506  
    /** Get the remote endpoint of the socket.
506  
    /** Get the remote endpoint of the socket.
507  

507  

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

510  

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

513  

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

521  

522  
private:
522  
private:
523  
    friend class tcp_acceptor;
523  
    friend class tcp_acceptor;
524  

524  

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

530  

531  
} // namespace boost::corosio
531  
} // namespace boost::corosio
532  

532  

533  
#endif
533  
#endif