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

10  

11  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
11  
#ifndef BOOST_COROSIO_TCP_SOCKET_HPP
12  
#define BOOST_COROSIO_TCP_SOCKET_HPP
12  
#define BOOST_COROSIO_TCP_SOCKET_HPP
13  

13  

14  
#include <boost/corosio/detail/config.hpp>
14  
#include <boost/corosio/detail/config.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
15  
#include <boost/corosio/detail/platform.hpp>
16  
#include <boost/corosio/detail/except.hpp>
16  
#include <boost/corosio/detail/except.hpp>
17  
#include <boost/corosio/detail/native_handle.hpp>
17  
#include <boost/corosio/detail/native_handle.hpp>
18  
#include <boost/corosio/io/io_stream.hpp>
18  
#include <boost/corosio/io/io_stream.hpp>
19  
#include <boost/capy/io_result.hpp>
19  
#include <boost/capy/io_result.hpp>
20  
#include <boost/corosio/detail/buffer_param.hpp>
20  
#include <boost/corosio/detail/buffer_param.hpp>
21  
#include <boost/corosio/endpoint.hpp>
21  
#include <boost/corosio/endpoint.hpp>
22  
#include <boost/corosio/tcp.hpp>
22  
#include <boost/corosio/tcp.hpp>
23  
#include <boost/capy/ex/executor_ref.hpp>
23  
#include <boost/capy/ex/executor_ref.hpp>
24  
#include <boost/capy/ex/execution_context.hpp>
24  
#include <boost/capy/ex/execution_context.hpp>
25  
#include <boost/capy/ex/io_env.hpp>
25  
#include <boost/capy/ex/io_env.hpp>
26  
#include <boost/capy/concept/executor.hpp>
26  
#include <boost/capy/concept/executor.hpp>
27  

27  

28  
#include <system_error>
28  
#include <system_error>
29  

29  

30  
#include <concepts>
30  
#include <concepts>
31  
#include <coroutine>
31  
#include <coroutine>
32  
#include <cstddef>
32  
#include <cstddef>
33  
#include <stop_token>
33  
#include <stop_token>
34  
#include <type_traits>
34  
#include <type_traits>
35  

35  

36  
namespace boost::corosio {
36  
namespace boost::corosio {
37  

37  

38  
/** An asynchronous TCP socket for coroutine I/O.
38  
/** An asynchronous TCP socket for coroutine I/O.
39  

39  

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

43  

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

47  

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

53  

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

58  

59  
    @par Example
59  
    @par Example
60  
    @code
60  
    @code
61  
    io_context ioc;
61  
    io_context ioc;
62  
    tcp_socket s(ioc);
62  
    tcp_socket s(ioc);
63  
    s.open();
63  
    s.open();
64  

64  

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

70  

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

86  

87  
    /** Define backend hooks for TCP socket operations.
87  
    /** Define backend hooks for TCP socket operations.
88  

88  

89  
        Platform backends (epoll, IOCP, kqueue, select) derive from
89  
        Platform backends (epoll, IOCP, kqueue, select) derive from
90  
        this to implement socket I/O, connection, and option management.
90  
        this to implement socket I/O, connection, and option management.
91  
    */
91  
    */
92  
    struct implementation : io_stream::implementation
92  
    struct implementation : io_stream::implementation
93  
    {
93  
    {
94  
        /** Initiate an asynchronous connect to the given endpoint.
94  
        /** Initiate an asynchronous connect to the given endpoint.
95  

95  

96  
            @param h Coroutine handle to resume on completion.
96  
            @param h Coroutine handle to resume on completion.
97  
            @param ex Executor for dispatching the completion.
97  
            @param ex Executor for dispatching the completion.
98  
            @param ep The remote endpoint to connect to.
98  
            @param ep The remote endpoint to connect to.
99  
            @param token Stop token for cancellation.
99  
            @param token Stop token for cancellation.
100  
            @param ec Output error code.
100  
            @param ec Output error code.
101  

101  

102  
            @return Coroutine handle to resume immediately.
102  
            @return Coroutine handle to resume immediately.
103  
        */
103  
        */
104  
        virtual std::coroutine_handle<> connect(
104  
        virtual std::coroutine_handle<> connect(
105  
            std::coroutine_handle<> h,
105  
            std::coroutine_handle<> h,
106  
            capy::executor_ref ex,
106  
            capy::executor_ref ex,
107  
            endpoint ep,
107  
            endpoint ep,
108  
            std::stop_token token,
108  
            std::stop_token token,
109  
            std::error_code* ec) = 0;
109  
            std::error_code* ec) = 0;
110  

110  

111  
        /** Shut down the socket for the given direction(s).
111  
        /** Shut down the socket for the given direction(s).
112  

112  

113  
            @param what The shutdown direction.
113  
            @param what The shutdown direction.
114  

114  

115  
            @return Error code on failure, empty on success.
115  
            @return Error code on failure, empty on success.
116  
        */
116  
        */
117  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
117  
        virtual std::error_code shutdown(shutdown_type what) noexcept = 0;
118  

118  

119  
        /// Return the platform socket descriptor.
119  
        /// Return the platform socket descriptor.
120  
        virtual native_handle_type native_handle() const noexcept = 0;
120  
        virtual native_handle_type native_handle() const noexcept = 0;
121  

121  

122  
        /** Request cancellation of pending asynchronous operations.
122  
        /** Request cancellation of pending asynchronous operations.
123  

123  

124  
            All outstanding operations complete with operation_canceled error.
124  
            All outstanding operations complete with operation_canceled error.
125  
            Check `ec == cond::canceled` for portable comparison.
125  
            Check `ec == cond::canceled` for portable comparison.
126  
        */
126  
        */
127  
        virtual void cancel() noexcept = 0;
127  
        virtual void cancel() noexcept = 0;
128  

128  

129  
        /** Set a socket option.
129  
        /** Set a socket option.
130  

130  

131  
            @param level The protocol level (e.g. `SOL_SOCKET`).
131  
            @param level The protocol level (e.g. `SOL_SOCKET`).
132  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
132  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
133  
            @param data Pointer to the option value.
133  
            @param data Pointer to the option value.
134  
            @param size Size of the option value in bytes.
134  
            @param size Size of the option value in bytes.
135  
            @return Error code on failure, empty on success.
135  
            @return Error code on failure, empty on success.
136  
        */
136  
        */
137  
        virtual std::error_code set_option(
137  
        virtual std::error_code set_option(
138  
            int level,
138  
            int level,
139  
            int optname,
139  
            int optname,
140  
            void const* data,
140  
            void const* data,
141  
            std::size_t size) noexcept = 0;
141  
            std::size_t size) noexcept = 0;
142  

142  

143  
        /** Get a socket option.
143  
        /** Get a socket option.
144  

144  

145  
            @param level The protocol level (e.g. `SOL_SOCKET`).
145  
            @param level The protocol level (e.g. `SOL_SOCKET`).
146  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
146  
            @param optname The option name (e.g. `SO_KEEPALIVE`).
147  
            @param data Pointer to receive the option value.
147  
            @param data Pointer to receive the option value.
148  
            @param size On entry, the size of the buffer. On exit,
148  
            @param size On entry, the size of the buffer. On exit,
149  
                the size of the option value.
149  
                the size of the option value.
150  
            @return Error code on failure, empty on success.
150  
            @return Error code on failure, empty on success.
151  
        */
151  
        */
152  
        virtual std::error_code
152  
        virtual std::error_code
153  
        get_option(int level, int optname, void* data, std::size_t* size)
153  
        get_option(int level, int optname, void* data, std::size_t* size)
154  
            const noexcept = 0;
154  
            const noexcept = 0;
155  

155  

156  
        /// Return the cached local endpoint.
156  
        /// Return the cached local endpoint.
157  
        virtual endpoint local_endpoint() const noexcept = 0;
157  
        virtual endpoint local_endpoint() const noexcept = 0;
158  

158  

159  
        /// Return the cached remote endpoint.
159  
        /// Return the cached remote endpoint.
160  
        virtual endpoint remote_endpoint() const noexcept = 0;
160  
        virtual endpoint remote_endpoint() const noexcept = 0;
161  
    };
161  
    };
162  

162  

163  
    /// Represent the awaitable returned by @ref connect.
163  
    /// Represent the awaitable returned by @ref connect.
164  
    struct connect_awaitable
164  
    struct connect_awaitable
165  
    {
165  
    {
166  
        tcp_socket& s_;
166  
        tcp_socket& s_;
167  
        endpoint endpoint_;
167  
        endpoint endpoint_;
168  
        std::stop_token token_;
168  
        std::stop_token token_;
169  
        mutable std::error_code ec_;
169  
        mutable std::error_code ec_;
170  

170  

171  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
171  
        connect_awaitable(tcp_socket& s, endpoint ep) noexcept
172  
            : s_(s)
172  
            : s_(s)
173  
            , endpoint_(ep)
173  
            , endpoint_(ep)
174  
        {
174  
        {
175  
        }
175  
        }
176  

176  

177  
        bool await_ready() const noexcept
177  
        bool await_ready() const noexcept
178  
        {
178  
        {
179  
            return token_.stop_requested();
179  
            return token_.stop_requested();
180  
        }
180  
        }
181  

181  

182  
        capy::io_result<> await_resume() const noexcept
182  
        capy::io_result<> await_resume() const noexcept
183  
        {
183  
        {
184  
            if (token_.stop_requested())
184  
            if (token_.stop_requested())
185  
                return {make_error_code(std::errc::operation_canceled)};
185  
                return {make_error_code(std::errc::operation_canceled)};
186  
            return {ec_};
186  
            return {ec_};
187  
        }
187  
        }
188  

188  

189  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
189  
        auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
190  
            -> std::coroutine_handle<>
190  
            -> std::coroutine_handle<>
191  
        {
191  
        {
192  
            token_ = env->stop_token;
192  
            token_ = env->stop_token;
193  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
193  
            return s_.get().connect(h, env->executor, endpoint_, token_, &ec_);
194  
        }
194  
        }
195  
    };
195  
    };
196  

196  

197  
public:
197  
public:
198  
    /** Destructor.
198  
    /** Destructor.
199  

199  

200  
        Closes the socket if open, cancelling any pending operations.
200  
        Closes the socket if open, cancelling any pending operations.
201  
    */
201  
    */
202  
    ~tcp_socket() override;
202  
    ~tcp_socket() override;
203  

203  

204  
    /** Construct a socket from an execution context.
204  
    /** Construct a socket from an execution context.
205  

205  

206  
        @param ctx The execution context that will own this socket.
206  
        @param ctx The execution context that will own this socket.
207  
    */
207  
    */
208  
    explicit tcp_socket(capy::execution_context& ctx);
208  
    explicit tcp_socket(capy::execution_context& ctx);
209  

209  

210  
    /** Construct a socket from an executor.
210  
    /** Construct a socket from an executor.
211  

211  

212  
        The socket is associated with the executor's context.
212  
        The socket is associated with the executor's context.
213  

213  

214  
        @param ex The executor whose context will own the socket.
214  
        @param ex The executor whose context will own the socket.
215  
    */
215  
    */
216  
    template<class Ex>
216  
    template<class Ex>
217  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
217  
        requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_socket>) &&
218  
        capy::Executor<Ex>
218  
        capy::Executor<Ex>
219  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
219  
    explicit tcp_socket(Ex const& ex) : tcp_socket(ex.context())
220  
    {
220  
    {
221  
    }
221  
    }
222  

222  

223  
    /** Move constructor.
223  
    /** Move constructor.
224  

224  

225  
        Transfers ownership of the socket resources.
225  
        Transfers ownership of the socket resources.
226  

226  

227  
        @param other The socket to move from.
227  
        @param other The socket to move from.
228  

228  

229  
        @pre No awaitables returned by @p other's methods exist.
229  
        @pre No awaitables returned by @p other's methods exist.
230  
        @pre @p other is not referenced as a peer in any outstanding
230  
        @pre @p other is not referenced as a peer in any outstanding
231  
            accept awaitable.
231  
            accept awaitable.
232  
        @pre The execution context associated with @p other must
232  
        @pre The execution context associated with @p other must
233  
            outlive this socket.
233  
            outlive this socket.
234  
    */
234  
    */
235  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
235  
    tcp_socket(tcp_socket&& other) noexcept : io_object(std::move(other)) {}
236  

236  

237  
    /** Move assignment operator.
237  
    /** Move assignment operator.
238  

238  

239  
        Closes any existing socket and transfers ownership.
239  
        Closes any existing socket and transfers ownership.
240  

240  

241  
        @param other The socket to move from.
241  
        @param other The socket to move from.
242  

242  

243  
        @pre No awaitables returned by either `*this` or @p other's
243  
        @pre No awaitables returned by either `*this` or @p other's
244  
            methods exist.
244  
            methods exist.
245  
        @pre Neither `*this` nor @p other is referenced as a peer in
245  
        @pre Neither `*this` nor @p other is referenced as a peer in
246  
            any outstanding accept awaitable.
246  
            any outstanding accept awaitable.
247  
        @pre The execution context associated with @p other must
247  
        @pre The execution context associated with @p other must
248  
            outlive this socket.
248  
            outlive this socket.
249  

249  

250  
        @return Reference to this socket.
250  
        @return Reference to this socket.
251  
    */
251  
    */
252  
    tcp_socket& operator=(tcp_socket&& other) noexcept
252  
    tcp_socket& operator=(tcp_socket&& other) noexcept
253  
    {
253  
    {
254  
        if (this != &other)
254  
        if (this != &other)
255  
        {
255  
        {
256  
            close();
256  
            close();
257  
            h_ = std::move(other.h_);
257  
            h_ = std::move(other.h_);
258  
        }
258  
        }
259  
        return *this;
259  
        return *this;
260  
    }
260  
    }
261  

261  

262  
    tcp_socket(tcp_socket const&)            = delete;
262  
    tcp_socket(tcp_socket const&)            = delete;
263  
    tcp_socket& operator=(tcp_socket const&) = delete;
263  
    tcp_socket& operator=(tcp_socket const&) = delete;
264  

264  

265  
    /** Open the socket.
265  
    /** Open the socket.
266  

266  

267  
        Creates a TCP socket and associates it with the platform
267  
        Creates a TCP socket and associates it with the platform
268  
        reactor (IOCP on Windows). Calling @ref connect on a closed
268  
        reactor (IOCP on Windows). Calling @ref connect on a closed
269  
        socket opens it automatically with the endpoint's address family,
269  
        socket opens it automatically with the endpoint's address family,
270  
        so explicit `open()` is only needed when socket options must be
270  
        so explicit `open()` is only needed when socket options must be
271  
        set before connecting.
271  
        set before connecting.
272  

272  

273  
        @param proto The protocol (IPv4 or IPv6). Defaults to
273  
        @param proto The protocol (IPv4 or IPv6). Defaults to
274  
            `tcp::v4()`.
274  
            `tcp::v4()`.
275  

275  

276  
        @throws std::system_error on failure.
276  
        @throws std::system_error on failure.
277  
    */
277  
    */
278  
    void open(tcp proto = tcp::v4());
278  
    void open(tcp proto = tcp::v4());
279  

279  

280  
    /** Close the socket.
280  
    /** Close the socket.
281  

281  

282  
        Releases socket resources. Any pending operations complete
282  
        Releases socket resources. Any pending operations complete
283  
        with `errc::operation_canceled`.
283  
        with `errc::operation_canceled`.
284  
    */
284  
    */
285  
    void close();
285  
    void close();
286  

286  

287  
    /** Check if the socket is open.
287  
    /** Check if the socket is open.
288  

288  

289  
        @return `true` if the socket is open and ready for operations.
289  
        @return `true` if the socket is open and ready for operations.
290  
    */
290  
    */
291  
    bool is_open() const noexcept
291  
    bool is_open() const noexcept
292  
    {
292  
    {
293  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
293  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
294  
        return h_ && get().native_handle() != ~native_handle_type(0);
294  
        return h_ && get().native_handle() != ~native_handle_type(0);
295  
#else
295  
#else
296  
        return h_ && get().native_handle() >= 0;
296  
        return h_ && get().native_handle() >= 0;
297  
#endif
297  
#endif
298  
    }
298  
    }
299  

299  

300  
    /** Initiate an asynchronous connect operation.
300  
    /** Initiate an asynchronous connect operation.
301  

301  

302  
        If the socket is not already open, it is opened automatically
302  
        If the socket is not already open, it is opened automatically
303  
        using the address family of @p ep (IPv4 or IPv6). If the socket
303  
        using the address family of @p ep (IPv4 or IPv6). If the socket
304  
        is already open, the existing file descriptor is used as-is.
304  
        is already open, the existing file descriptor is used as-is.
305  

305  

306  
        The operation supports cancellation via `std::stop_token` through
306  
        The operation supports cancellation via `std::stop_token` through
307  
        the affine awaitable protocol. If the associated stop token is
307  
        the affine awaitable protocol. If the associated stop token is
308  
        triggered, the operation completes immediately with
308  
        triggered, the operation completes immediately with
309  
        `errc::operation_canceled`.
309  
        `errc::operation_canceled`.
310  

310  

311  
        @param ep The remote endpoint to connect to.
311  
        @param ep The remote endpoint to connect to.
312  

312  

313  
        @return An awaitable that completes with `io_result<>`.
313  
        @return An awaitable that completes with `io_result<>`.
314  
            Returns success (default error_code) on successful connection,
314  
            Returns success (default error_code) on successful connection,
315  
            or an error code on failure including:
315  
            or an error code on failure including:
316  
            - connection_refused: No server listening at endpoint
316  
            - connection_refused: No server listening at endpoint
317  
            - timed_out: Connection attempt timed out
317  
            - timed_out: Connection attempt timed out
318  
            - network_unreachable: No route to host
318  
            - network_unreachable: No route to host
319  
            - operation_canceled: Cancelled via stop_token or cancel().
319  
            - operation_canceled: Cancelled via stop_token or cancel().
320  
                Check `ec == cond::canceled` for portable comparison.
320  
                Check `ec == cond::canceled` for portable comparison.
321  

321  

322  
        @throws std::system_error if the socket needs to be opened
322  
        @throws std::system_error if the socket needs to be opened
323  
            and the open fails.
323  
            and the open fails.
324  

324  

325  
        @par Preconditions
325  
        @par Preconditions
326  
        This socket must outlive the returned awaitable.
326  
        This socket must outlive the returned awaitable.
327  

327  

328  
        @par Example
328  
        @par Example
329  
        @code
329  
        @code
330  
        // Socket opened automatically with correct address family:
330  
        // Socket opened automatically with correct address family:
331  
        auto [ec] = co_await s.connect(endpoint);
331  
        auto [ec] = co_await s.connect(endpoint);
332  
        if (ec) { ... }
332  
        if (ec) { ... }
333  
        @endcode
333  
        @endcode
334  
    */
334  
    */
335  
    auto connect(endpoint ep)
335  
    auto connect(endpoint ep)
336  
    {
336  
    {
337  
        if (!is_open())
337  
        if (!is_open())
338  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
338  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
339  
        return connect_awaitable(*this, ep);
339  
        return connect_awaitable(*this, ep);
340  
    }
340  
    }
341  

341  

342  
    /** Cancel any pending asynchronous operations.
342  
    /** Cancel any pending asynchronous operations.
343  

343  

344  
        All outstanding operations complete with `errc::operation_canceled`.
344  
        All outstanding operations complete with `errc::operation_canceled`.
345  
        Check `ec == cond::canceled` for portable comparison.
345  
        Check `ec == cond::canceled` for portable comparison.
346  
    */
346  
    */
347  
    void cancel();
347  
    void cancel();
348  

348  

349  
    /** Get the native socket handle.
349  
    /** Get the native socket handle.
350  

350  

351  
        Returns the underlying platform-specific socket descriptor.
351  
        Returns the underlying platform-specific socket descriptor.
352  
        On POSIX systems this is an `int` file descriptor.
352  
        On POSIX systems this is an `int` file descriptor.
353  
        On Windows this is a `SOCKET` handle.
353  
        On Windows this is a `SOCKET` handle.
354  

354  

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

356  

357  
        @par Preconditions
357  
        @par Preconditions
358  
        None. May be called on closed sockets.
358  
        None. May be called on closed sockets.
359  
    */
359  
    */
360  
    native_handle_type native_handle() const noexcept;
360  
    native_handle_type native_handle() const noexcept;
361  

361  

362  
    /** Disable sends or receives on the socket.
362  
    /** Disable sends or receives on the socket.
363  

363  

364  
        TCP connections are full-duplex: each direction (send and receive)
364  
        TCP connections are full-duplex: each direction (send and receive)
365  
        operates independently. This function allows you to close one or
365  
        operates independently. This function allows you to close one or
366  
        both directions without destroying the socket.
366  
        both directions without destroying the socket.
367  

367  

368  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
368  
        @li @ref shutdown_send sends a TCP FIN packet to the peer,
369  
            signaling that you have no more data to send. You can still
369  
            signaling that you have no more data to send. You can still
370  
            receive data until the peer also closes their send direction.
370  
            receive data until the peer also closes their send direction.
371  
            This is the most common use case, typically called before
371  
            This is the most common use case, typically called before
372  
            close() to ensure graceful connection termination.
372  
            close() to ensure graceful connection termination.
373  

373  

374  
        @li @ref shutdown_receive disables reading on the socket. This
374  
        @li @ref shutdown_receive disables reading on the socket. This
375  
            does NOT send anything to the peer - they are not informed
375  
            does NOT send anything to the peer - they are not informed
376  
            and may continue sending data. Subsequent reads will fail
376  
            and may continue sending data. Subsequent reads will fail
377  
            or return end-of-file. Incoming data may be discarded or
377  
            or return end-of-file. Incoming data may be discarded or
378  
            buffered depending on the operating system.
378  
            buffered depending on the operating system.
379  

379  

380  
        @li @ref shutdown_both combines both effects: sends a FIN and
380  
        @li @ref shutdown_both combines both effects: sends a FIN and
381  
            disables reading.
381  
            disables reading.
382  

382  

383  
        When the peer shuts down their send direction (sends a FIN),
383  
        When the peer shuts down their send direction (sends a FIN),
384  
        subsequent read operations will complete with `capy::cond::eof`.
384  
        subsequent read operations will complete with `capy::cond::eof`.
385  
        Use the portable condition test rather than comparing error
385  
        Use the portable condition test rather than comparing error
386  
        codes directly:
386  
        codes directly:
387  

387  

388  
        @code
388  
        @code
389  
        auto [ec, n] = co_await sock.read_some(buffer);
389  
        auto [ec, n] = co_await sock.read_some(buffer);
390  
        if (ec == capy::cond::eof)
390  
        if (ec == capy::cond::eof)
391  
        {
391  
        {
392  
            // Peer closed their send direction
392  
            // Peer closed their send direction
393  
        }
393  
        }
394  
        @endcode
394  
        @endcode
395  

395  

396  
        Any error from the underlying system call is silently discarded
396  
        Any error from the underlying system call is silently discarded
397  
        because it is unlikely to be helpful.
397  
        because it is unlikely to be helpful.
398  

398  

399  
        @param what Determines what operations will no longer be allowed.
399  
        @param what Determines what operations will no longer be allowed.
400  
    */
400  
    */
401  
    void shutdown(shutdown_type what);
401  
    void shutdown(shutdown_type what);
402  

402  

403  
    /** Set a socket option.
403  
    /** Set a socket option.
404  

404  

405  
        Applies a type-safe socket option to the underlying socket.
405  
        Applies a type-safe socket option to the underlying socket.
406  
        The option type encodes the protocol level and option name.
406  
        The option type encodes the protocol level and option name.
407  

407  

408  
        @par Example
408  
        @par Example
409  
        @code
409  
        @code
410  
        sock.set_option( socket_option::no_delay( true ) );
410  
        sock.set_option( socket_option::no_delay( true ) );
411  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
411  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
412  
        @endcode
412  
        @endcode
413  

413  

414  
        @param opt The option to set.
414  
        @param opt The option to set.
415  

415  

416  
        @throws std::logic_error if the socket is not open.
416  
        @throws std::logic_error if the socket is not open.
417  
        @throws std::system_error on failure.
417  
        @throws std::system_error on failure.
418  
    */
418  
    */
419  
    template<class Option>
419  
    template<class Option>
420  
    void set_option(Option const& opt)
420  
    void set_option(Option const& opt)
421  
    {
421  
    {
422  
        if (!is_open())
422  
        if (!is_open())
423  
            detail::throw_logic_error("set_option: socket not open");
423  
            detail::throw_logic_error("set_option: socket not open");
424  
        std::error_code ec = get().set_option(
424  
        std::error_code ec = get().set_option(
425  
            Option::level(), Option::name(), opt.data(), opt.size());
425  
            Option::level(), Option::name(), opt.data(), opt.size());
426  
        if (ec)
426  
        if (ec)
427  
            detail::throw_system_error(ec, "tcp_socket::set_option");
427  
            detail::throw_system_error(ec, "tcp_socket::set_option");
428  
    }
428  
    }
429  

429  

430  
    /** Get a socket option.
430  
    /** Get a socket option.
431  

431  

432  
        Retrieves the current value of a type-safe socket option.
432  
        Retrieves the current value of a type-safe socket option.
433  

433  

434  
        @par Example
434  
        @par Example
435  
        @code
435  
        @code
436  
        auto nd = sock.get_option<socket_option::no_delay>();
436  
        auto nd = sock.get_option<socket_option::no_delay>();
437  
        if ( nd.value() )
437  
        if ( nd.value() )
438  
            // Nagle's algorithm is disabled
438  
            // Nagle's algorithm is disabled
439  
        @endcode
439  
        @endcode
440  

440  

441  
        @return The current option value.
441  
        @return The current option value.
442  

442  

443  
        @throws std::logic_error if the socket is not open.
443  
        @throws std::logic_error if the socket is not open.
444  
        @throws std::system_error on failure.
444  
        @throws std::system_error on failure.
445  
    */
445  
    */
446  
    template<class Option>
446  
    template<class Option>
447  
    Option get_option() const
447  
    Option get_option() const
448  
    {
448  
    {
449  
        if (!is_open())
449  
        if (!is_open())
450  
            detail::throw_logic_error("get_option: socket not open");
450  
            detail::throw_logic_error("get_option: socket not open");
451  
        Option opt{};
451  
        Option opt{};
452  
        std::size_t sz = opt.size();
452  
        std::size_t sz = opt.size();
453  
        std::error_code ec =
453  
        std::error_code ec =
454  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
454  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
455  
        if (ec)
455  
        if (ec)
456  
            detail::throw_system_error(ec, "tcp_socket::get_option");
456  
            detail::throw_system_error(ec, "tcp_socket::get_option");
457  
        opt.resize(sz);
457  
        opt.resize(sz);
458  
        return opt;
458  
        return opt;
459  
    }
459  
    }
460  

460  

461  
    /** Get the local endpoint of the socket.
461  
    /** Get the local endpoint of the socket.
462  

462  

463  
        Returns the local address and port to which the socket is bound.
463  
        Returns the local address and port to which the socket is bound.
464  
        For a connected socket, this is the local side of the connection.
464  
        For a connected socket, this is the local side of the connection.
465  
        The endpoint is cached when the connection is established.
465  
        The endpoint is cached when the connection is established.
466  

466  

467  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
467  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
468  
            the socket is not connected.
468  
            the socket is not connected.
469  

469  

470  
        @par Thread Safety
470  
        @par Thread Safety
471  
        The cached endpoint value is set during connect/accept completion
471  
        The cached endpoint value is set during connect/accept completion
472  
        and cleared during close(). This function may be called concurrently
472  
        and cleared during close(). This function may be called concurrently
473  
        with I/O operations, but must not be called concurrently with
473  
        with I/O operations, but must not be called concurrently with
474  
        connect(), accept(), or close().
474  
        connect(), accept(), or close().
475  
    */
475  
    */
476  
    endpoint local_endpoint() const noexcept;
476  
    endpoint local_endpoint() const noexcept;
477  

477  

478  
    /** Get the remote endpoint of the socket.
478  
    /** Get the remote endpoint of the socket.
479  

479  

480  
        Returns the remote address and port to which the socket is connected.
480  
        Returns the remote address and port to which the socket is connected.
481  
        The endpoint is cached when the connection is established.
481  
        The endpoint is cached when the connection is established.
482  

482  

483  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
483  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
484  
            the socket is not connected.
484  
            the socket is not connected.
485  

485  

486  
        @par Thread Safety
486  
        @par Thread Safety
487  
        The cached endpoint value is set during connect/accept completion
487  
        The cached endpoint value is set during connect/accept completion
488  
        and cleared during close(). This function may be called concurrently
488  
        and cleared during close(). This function may be called concurrently
489  
        with I/O operations, but must not be called concurrently with
489  
        with I/O operations, but must not be called concurrently with
490  
        connect(), accept(), or close().
490  
        connect(), accept(), or close().
491  
    */
491  
    */
492  
    endpoint remote_endpoint() const noexcept;
492  
    endpoint remote_endpoint() const noexcept;
493  

493  

494  
protected:
494  
protected:
495  
    tcp_socket() noexcept = default;
495  
    tcp_socket() noexcept = default;
496  

496  

497  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
497  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
498  

498  

499  
private:
499  
private:
500  
    friend class tcp_acceptor;
500  
    friend class tcp_acceptor;
501  

501  

502  
    /// Open the socket for the given protocol triple.
502  
    /// Open the socket for the given protocol triple.
503  
    void open_for_family(int family, int type, int protocol);
503  
    void open_for_family(int family, int type, int protocol);
504  

504  

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

510  

511  
} // namespace boost::corosio
511  
} // namespace boost::corosio
512  

512  

513  
#endif
513  
#endif