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/detail/op_base.hpp>
18  
#include <boost/corosio/io/io_stream.hpp>
19  
#include <boost/corosio/io/io_stream.hpp>
19  
#include <boost/capy/io_result.hpp>
20  
#include <boost/capy/io_result.hpp>
20  
#include <boost/corosio/detail/buffer_param.hpp>
21  
#include <boost/corosio/detail/buffer_param.hpp>
21  
#include <boost/corosio/endpoint.hpp>
22  
#include <boost/corosio/endpoint.hpp>
 
23 +
#include <boost/corosio/shutdown_type.hpp>
22  
#include <boost/corosio/tcp.hpp>
24  
#include <boost/corosio/tcp.hpp>
23  
#include <boost/capy/ex/executor_ref.hpp>
25  
#include <boost/capy/ex/executor_ref.hpp>
24  
#include <boost/capy/ex/execution_context.hpp>
26  
#include <boost/capy/ex/execution_context.hpp>
25  
#include <boost/capy/ex/io_env.hpp>
27  
#include <boost/capy/ex/io_env.hpp>
26  
#include <boost/capy/concept/executor.hpp>
28  
#include <boost/capy/concept/executor.hpp>
27  

29  

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

31  

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

37  

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

39  

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

41  

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

45  

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

49  

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

55  

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

60  

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

66  

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

72  

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

83  

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

85  

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

92  

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

98  

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

107  

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

109  

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

111  

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

115  

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

118  

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

120  

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

125  

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

127  

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

139  

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

141  

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

152  

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

155  

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

159  

163  
    /// Represent the awaitable returned by @ref connect.
160  
    /// Represent the awaitable returned by @ref connect.
164  
    struct connect_awaitable
161  
    struct connect_awaitable
 
162 +
        : detail::void_op_base<connect_awaitable>
165  
    {
163  
    {
166  
        tcp_socket& s_;
164  
        tcp_socket& s_;
167 -
        std::stop_token token_;
 
168 -
        mutable std::error_code ec_;
 
169  
        endpoint endpoint_;
165  
        endpoint endpoint_;
170  

166  

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

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

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

169  

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

176  

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

179  

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

183  

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

185  

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

189  

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

191  

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

193  

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

202  

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

204  

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

206  

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

208  

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

216  

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

218  

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

220  

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

222  

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

229  

250  
        @return Reference to this socket.
230  
        @return Reference to this socket.
251  
    */
231  
    */
252  
    tcp_socket& operator=(tcp_socket&& other) noexcept
232  
    tcp_socket& operator=(tcp_socket&& other) noexcept
253  
    {
233  
    {
254  
        if (this != &other)
234  
        if (this != &other)
255  
        {
235  
        {
256  
            close();
236  
            close();
257  
            h_ = std::move(other.h_);
237  
            h_ = std::move(other.h_);
258  
        }
238  
        }
259  
        return *this;
239  
        return *this;
260  
    }
240  
    }
261  

241  

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

244  

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

246  

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

252  

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

255  

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

259  

280  
    /** Bind the socket to a local endpoint.
260  
    /** Bind the socket to a local endpoint.
281  

261  

282  
        Associates the socket with a local address and port before
262  
        Associates the socket with a local address and port before
283  
        connecting. Useful for multi-homed hosts or source-port
263  
        connecting. Useful for multi-homed hosts or source-port
284  
        pinning.
264  
        pinning.
285  

265  

286  
        @param ep The local endpoint to bind to.
266  
        @param ep The local endpoint to bind to.
287  

267  

288  
        @return An error code indicating success or the reason for
268  
        @return An error code indicating success or the reason for
289  
            failure.
269  
            failure.
290  

270  

291  
        @par Error Conditions
271  
        @par Error Conditions
292  
        @li `errc::address_in_use`: The endpoint is already in use.
272  
        @li `errc::address_in_use`: The endpoint is already in use.
293  
        @li `errc::address_not_available`: The address is not
273  
        @li `errc::address_not_available`: The address is not
294  
            available on any local interface.
274  
            available on any local interface.
295  
        @li `errc::permission_denied`: Insufficient privileges to
275  
        @li `errc::permission_denied`: Insufficient privileges to
296  
            bind to the endpoint (e.g., privileged port).
276  
            bind to the endpoint (e.g., privileged port).
297  

277  

298  
        @throws std::logic_error if the socket is not open.
278  
        @throws std::logic_error if the socket is not open.
299  
    */
279  
    */
300  
    [[nodiscard]] std::error_code bind(endpoint ep);
280  
    [[nodiscard]] std::error_code bind(endpoint ep);
301  

281  

302  
    /** Close the socket.
282  
    /** Close the socket.
303  

283  

304  
        Releases socket resources. Any pending operations complete
284  
        Releases socket resources. Any pending operations complete
305  
        with `errc::operation_canceled`.
285  
        with `errc::operation_canceled`.
306  
    */
286  
    */
307  
    void close();
287  
    void close();
308  

288  

309  
    /** Check if the socket is open.
289  
    /** Check if the socket is open.
310  

290  

311  
        @return `true` if the socket is open and ready for operations.
291  
        @return `true` if the socket is open and ready for operations.
312  
    */
292  
    */
313  
    bool is_open() const noexcept
293  
    bool is_open() const noexcept
314  
    {
294  
    {
315  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
295  
#if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
316  
        return h_ && get().native_handle() != ~native_handle_type(0);
296  
        return h_ && get().native_handle() != ~native_handle_type(0);
317  
#else
297  
#else
318  
        return h_ && get().native_handle() >= 0;
298  
        return h_ && get().native_handle() >= 0;
319  
#endif
299  
#endif
320  
    }
300  
    }
321  

301  

322  
    /** Initiate an asynchronous connect operation.
302  
    /** Initiate an asynchronous connect operation.
323  

303  

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

307  

328  
        The operation supports cancellation via `std::stop_token` through
308  
        The operation supports cancellation via `std::stop_token` through
329  
        the affine awaitable protocol. If the associated stop token is
309  
        the affine awaitable protocol. If the associated stop token is
330  
        triggered, the operation completes immediately with
310  
        triggered, the operation completes immediately with
331  
        `errc::operation_canceled`.
311  
        `errc::operation_canceled`.
332  

312  

333  
        @param ep The remote endpoint to connect to.
313  
        @param ep The remote endpoint to connect to.
334  

314  

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

323  

344  
        @throws std::system_error if the socket needs to be opened
324  
        @throws std::system_error if the socket needs to be opened
345  
            and the open fails.
325  
            and the open fails.
346  

326  

347  
        @par Preconditions
327  
        @par Preconditions
348  
        This socket must outlive the returned awaitable.
328  
        This socket must outlive the returned awaitable.
349  

329  

350  
        @par Example
330  
        @par Example
351  
        @code
331  
        @code
352  
        // Socket opened automatically with correct address family:
332  
        // Socket opened automatically with correct address family:
353  
        auto [ec] = co_await s.connect(endpoint);
333  
        auto [ec] = co_await s.connect(endpoint);
354  
        if (ec) { ... }
334  
        if (ec) { ... }
355  
        @endcode
335  
        @endcode
356  
    */
336  
    */
357  
    auto connect(endpoint ep)
337  
    auto connect(endpoint ep)
358  
    {
338  
    {
359  
        if (!is_open())
339  
        if (!is_open())
360  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
340  
            open(ep.is_v6() ? tcp::v6() : tcp::v4());
361  
        return connect_awaitable(*this, ep);
341  
        return connect_awaitable(*this, ep);
362  
    }
342  
    }
363  

343  

364  
    /** Cancel any pending asynchronous operations.
344  
    /** Cancel any pending asynchronous operations.
365  

345  

366  
        All outstanding operations complete with `errc::operation_canceled`.
346  
        All outstanding operations complete with `errc::operation_canceled`.
367  
        Check `ec == cond::canceled` for portable comparison.
347  
        Check `ec == cond::canceled` for portable comparison.
368  
    */
348  
    */
369  
    void cancel();
349  
    void cancel();
370  

350  

371  
    /** Get the native socket handle.
351  
    /** Get the native socket handle.
372  

352  

373  
        Returns the underlying platform-specific socket descriptor.
353  
        Returns the underlying platform-specific socket descriptor.
374  
        On POSIX systems this is an `int` file descriptor.
354  
        On POSIX systems this is an `int` file descriptor.
375  
        On Windows this is a `SOCKET` handle.
355  
        On Windows this is a `SOCKET` handle.
376  

356  

377  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
357  
        @return The native socket handle, or -1/INVALID_SOCKET if not open.
378  

358  

379  
        @par Preconditions
359  
        @par Preconditions
380  
        None. May be called on closed sockets.
360  
        None. May be called on closed sockets.
381  
    */
361  
    */
382  
    native_handle_type native_handle() const noexcept;
362  
    native_handle_type native_handle() const noexcept;
383  

363  

384  
    /** Disable sends or receives on the socket.
364  
    /** Disable sends or receives on the socket.
385  

365  

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

369  

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

375  

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

381  

402  
        @li @ref shutdown_both combines both effects: sends a FIN and
382  
        @li @ref shutdown_both combines both effects: sends a FIN and
403  
            disables reading.
383  
            disables reading.
404  

384  

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

389  

410  
        @code
390  
        @code
411  
        auto [ec, n] = co_await sock.read_some(buffer);
391  
        auto [ec, n] = co_await sock.read_some(buffer);
412  
        if (ec == capy::cond::eof)
392  
        if (ec == capy::cond::eof)
413  
        {
393  
        {
414  
            // Peer closed their send direction
394  
            // Peer closed their send direction
415  
        }
395  
        }
416  
        @endcode
396  
        @endcode
417  

397  

418  
        Any error from the underlying system call is silently discarded
398  
        Any error from the underlying system call is silently discarded
419  
        because it is unlikely to be helpful.
399  
        because it is unlikely to be helpful.
420  

400  

421  
        @param what Determines what operations will no longer be allowed.
401  
        @param what Determines what operations will no longer be allowed.
422  
    */
402  
    */
423  
    void shutdown(shutdown_type what);
403  
    void shutdown(shutdown_type what);
424  

404  

425  
    /** Set a socket option.
405  
    /** Set a socket option.
426  

406  

427  
        Applies a type-safe socket option to the underlying socket.
407  
        Applies a type-safe socket option to the underlying socket.
428  
        The option type encodes the protocol level and option name.
408  
        The option type encodes the protocol level and option name.
429  

409  

430  
        @par Example
410  
        @par Example
431  
        @code
411  
        @code
432  
        sock.set_option( socket_option::no_delay( true ) );
412  
        sock.set_option( socket_option::no_delay( true ) );
433  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
413  
        sock.set_option( socket_option::receive_buffer_size( 65536 ) );
434  
        @endcode
414  
        @endcode
435  

415  

436  
        @param opt The option to set.
416  
        @param opt The option to set.
437  

417  

438  
        @throws std::logic_error if the socket is not open.
418  
        @throws std::logic_error if the socket is not open.
439  
        @throws std::system_error on failure.
419  
        @throws std::system_error on failure.
440  
    */
420  
    */
441  
    template<class Option>
421  
    template<class Option>
442  
    void set_option(Option const& opt)
422  
    void set_option(Option const& opt)
443  
    {
423  
    {
444  
        if (!is_open())
424  
        if (!is_open())
445  
            detail::throw_logic_error("set_option: socket not open");
425  
            detail::throw_logic_error("set_option: socket not open");
446  
        std::error_code ec = get().set_option(
426  
        std::error_code ec = get().set_option(
447  
            Option::level(), Option::name(), opt.data(), opt.size());
427  
            Option::level(), Option::name(), opt.data(), opt.size());
448  
        if (ec)
428  
        if (ec)
449  
            detail::throw_system_error(ec, "tcp_socket::set_option");
429  
            detail::throw_system_error(ec, "tcp_socket::set_option");
450  
    }
430  
    }
451  

431  

452  
    /** Get a socket option.
432  
    /** Get a socket option.
453  

433  

454  
        Retrieves the current value of a type-safe socket option.
434  
        Retrieves the current value of a type-safe socket option.
455  

435  

456  
        @par Example
436  
        @par Example
457  
        @code
437  
        @code
458  
        auto nd = sock.get_option<socket_option::no_delay>();
438  
        auto nd = sock.get_option<socket_option::no_delay>();
459  
        if ( nd.value() )
439  
        if ( nd.value() )
460  
            // Nagle's algorithm is disabled
440  
            // Nagle's algorithm is disabled
461  
        @endcode
441  
        @endcode
462  

442  

463  
        @return The current option value.
443  
        @return The current option value.
464  

444  

465  
        @throws std::logic_error if the socket is not open.
445  
        @throws std::logic_error if the socket is not open.
466  
        @throws std::system_error on failure.
446  
        @throws std::system_error on failure.
467  
    */
447  
    */
468  
    template<class Option>
448  
    template<class Option>
469  
    Option get_option() const
449  
    Option get_option() const
470  
    {
450  
    {
471  
        if (!is_open())
451  
        if (!is_open())
472  
            detail::throw_logic_error("get_option: socket not open");
452  
            detail::throw_logic_error("get_option: socket not open");
473  
        Option opt{};
453  
        Option opt{};
474  
        std::size_t sz = opt.size();
454  
        std::size_t sz = opt.size();
475  
        std::error_code ec =
455  
        std::error_code ec =
476  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
456  
            get().get_option(Option::level(), Option::name(), opt.data(), &sz);
477  
        if (ec)
457  
        if (ec)
478  
            detail::throw_system_error(ec, "tcp_socket::get_option");
458  
            detail::throw_system_error(ec, "tcp_socket::get_option");
479  
        opt.resize(sz);
459  
        opt.resize(sz);
480  
        return opt;
460  
        return opt;
481  
    }
461  
    }
482  

462  

483  
    /** Get the local endpoint of the socket.
463  
    /** Get the local endpoint of the socket.
484  

464  

485  
        Returns the local address and port to which the socket is bound.
465  
        Returns the local address and port to which the socket is bound.
486  
        For a connected socket, this is the local side of the connection.
466  
        For a connected socket, this is the local side of the connection.
487  
        The endpoint is cached when the connection is established.
467  
        The endpoint is cached when the connection is established.
488  

468  

489  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
469  
        @return The local endpoint, or a default endpoint (0.0.0.0:0) if
490  
            the socket is not connected.
470  
            the socket is not connected.
491  

471  

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

479  

500  
    /** Get the remote endpoint of the socket.
480  
    /** Get the remote endpoint of the socket.
501  

481  

502  
        Returns the remote address and port to which the socket is connected.
482  
        Returns the remote address and port to which the socket is connected.
503  
        The endpoint is cached when the connection is established.
483  
        The endpoint is cached when the connection is established.
504  

484  

505  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
485  
        @return The remote endpoint, or a default endpoint (0.0.0.0:0) if
506  
            the socket is not connected.
486  
            the socket is not connected.
507  

487  

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

495  

516  
protected:
496  
protected:
517  
    tcp_socket() noexcept = default;
497  
    tcp_socket() noexcept = default;
518  

498  

519  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
499  
    explicit tcp_socket(handle h) noexcept : io_object(std::move(h)) {}
520  

500  

521  
private:
501  
private:
522  
    friend class tcp_acceptor;
502  
    friend class tcp_acceptor;
523  

503  

524  
    /// Open the socket for the given protocol triple.
504  
    /// Open the socket for the given protocol triple.
525  
    void open_for_family(int family, int type, int protocol);
505  
    void open_for_family(int family, int type, int protocol);
526  

506  

527  
    inline implementation& get() const noexcept
507  
    inline implementation& get() const noexcept
528  
    {
508  
    {
529  
        return *static_cast<implementation*>(h_.get());
509  
        return *static_cast<implementation*>(h_.get());
530  
    }
510  
    }
531  
};
511  
};
532  

512  

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

514  

535  
#endif
515  
#endif