Document number: P1322R3
Date:            2021-02-05
Project:         Programming Language C++
Audience:        SG4 - Networking, LEWG
Reply-to:        Christopher Kohlhoff <chris@kohlhoff.com>

Networking TS enhancement to enable custom I/O executors

1. Introduction

At present, the Networking TS's I/O objects -- sockets, acceptors, resolvers, and timers -- can be associated only with concrete execution contexts of type io_context. This paper proposes a minor change to the specification to permit the construction of these objects with arbitrary I/O executors and execution contexts. This would:

2. Rationale

Construction with arbitrary execution contexts

Users and third-party libraries may implement their own execution contexts, for example to provide different scheduling guarantees, or to natively support other types of I/O objects, such as files. This proposal would allow users to create the standard I/O objects, such as sockets, to be associated with these custom execution contexts. However, although constructing the socket with a custom execution context will provide the correct, expected behaviour, there is no guarantee that it will operate as efficiently as a socket associated with a native execution context.

Support for multiple native execution contexts

Some operating systems have multiple potential implementation strategies for the I/O objects, like sockets. For example, on Windows we can choose to have asynchronous notifications delivered via a completion port to a user-created thread, or we can have them delivered directly to the system thread pool. An implementer may want to make both of these strategies available, via the io_context and system_context execution contexts, respectively. For example:

  io_context my_context;
  tcp::socket my_socket_1(my_context); // Implementation uses a completion port.

  // ...

  tcp::socket my_socket_2(system_executor{}); // Implementation uses the system thread pool.

Libraries can also provide additional native execution contexts as implementation-specific extensions.

Convenient construction with a preferred executor

In some use cases, all asynchronous operations on a given I/O object are performed using the same executor. One common example of this is when using a strand. This proposal allows this executor to be specified once, when the I/O object is constructed. For example:

  io_context my_context;

  // ...

  strand<io_context::executor_type> my_strand(my_context.get_executor());
  tcp::socket my_socket(my_strand);

  // ...

  my_socket.async_receive(my_buffer, my_handler); // my_handler is invoked in the strand

Vendor- or user-specialization of I/O object templates

The Executor template parameter also offers an opportunity to specialize the I/O object types for specific executor types. For example, some platforms support vendor-specific kernel bypass APIs for I/O such as sockets, enabling significantly lower latency and higher throughput when compared to system calls. Furthermore, these APIs are often limited to supporting only TCP and UDP. A library or user may therefore choose to define an executor type that represents this API as a venue for I/O execution:

  class vendor_xyz_kernel_bypass_executor
  {
    // ...
  };

and specialize the socket templates for this executor type:

  template<>
  class basic_socket<ip::tcp, vendor_xyz_kernel_bypass_executor>
  {
    // ...
  };

  template<>
  class basic_datagram_socket<ip::tcp, vendor_xyz_kernel_bypass_executor>
    : public basic_socket<ip::tcp, vendor_xyz_kernel_bypass_executor>
  {
    // ...
    template<class ConstBufferSequence>
    void send(const ConstBufferSequence& buffers)
    {
      // Implemented using vendor_xyz_send().
      // ...
    }
    // ...
  };

3. Overview of changes to specification

Addition of Executor template parameter

A new Executor template parameter is added to the following templates:

To enable convenient interoperability with arbitrary executors and execution contexts, the template parameter is defaulted to the executor polymorphic wrapper. For example:

  template <class Protocol, class Executor = executor>
    class basic_socket;

For each of:

the existing nested type executor_type is altered to refer to the Executor type:

  template <class Protocol, class Executor>
  class basic_socket : public socket_base
  {
  public:
    using executor_type = io_context::executor_typeExecutor;
    // ...
  };

For the remaining four templates:

the nested type executor_type is added:

  template <class Protocol, class Executor>
  class basic_stream_socket : public basic_socket<Protocol, Executor>
  {
  public:
    using executor_type = Executor;
    // ...
  };

Addition of nested template type alias rebind_executor

For each of the following templates:

a nested template type alias rebind_executor is added:

  template<class Protocol, class Executor>
  class basic_socket : public socket_base
  {
    // ...

    /// The socket type when rebound to the specified executor.
    template<class OtherExecutor>
    using rebind_executor =
      basic_socket<Protocol, OtherExecutor>;

    // ...
  };

This enables computation of the appropriate class type for different I/O executor types.

Modification of I/O objects' constructors

For each of the following templates:

every constructor with an io_context& parameter is replaced with two constructors:

  template<class Protocol, class Executor>
  class basic_socket : public socket_base
  {
    // ...
    basic_socket(io_context& ctx, const protocol_type& protocol);
    basic_socket(const executor_type& ex, const protocol_type& protocol);
    template<class ExecutionContext>
      basic_socket(ExecutionContext& ctx, const protocol_type& protocol);
    // ...
  };

The second of these constructors shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true, and is_constructible<executor_type, typename ExecutionContext::executor_type>::value is true.

Modification of I/O objects' converting constructors

An OtherExecutor template parameter is added as required to the converting move constructors of the following classes:

For example:

  template<class Protocol, class Executor>
  class basic_socket : public socket_base
  {
    // ...
    template<class OtherProtocol, class OtherExecutor>
      basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
    // ...
  };

This constructor shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol, and OtherExecutor is implicitly convertible to Executor.

Modification of I/O objects' converting assignment operators

An OtherExecutor template parameter is added as required to the converting move assignment operators of the following classes:

For example:

  template<class Protocol, class Executor>
  class basic_socket : public socket_base
  {
    // ...
    template<class OtherProtocol, class OtherExecutor>
      basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
    // ...
  };

This assignment operator shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol, and OtherExecutor is implicitly convertible to Executor.

Addition of nested type alias socket_for to AcceptableProtocol requirements and protocol classes

The AcceptableProtocol type requirements, ip::tcp class, and ip::udp class each acquire a new nested template type alias socket_for. It can be used to create a protocol socket with a specific executor type:

  net::ip::tcp::socket_for<my_executor> sock = /* ... */;

Addition of template type alias to basic_socket_acceptor for using a socket with a specific executor

The template basic_socket_acceptor gains a new nested template type alias socket_type_for:

  template<class AcceptableProtocol, class Executor>
  class basic_socket_acceptor : public socket_base
  {
    // ...

    template<class OtherExecutor>
    using socket_type_for = typename AcceptableProtocol::socket_for<OtherExecutor>;

    // ...
  };

This enables computation of the appropriate socket type produced when accepting on different I/O executor types.

Modification of basic_socket_acceptor member functions

Every overload of member functions accept and async_accept with an io_context& parameter is replaced with two overloads:

  template<class AcceptableProtocol, class Executor>
  class basic_socket_acceptor : public socket_base
  {
    // ...
    socket_type accept(io_context& ctx);
    template<class OtherExecutor>
      socket_type_for<OtherExecutor>
        accept(const OtherExecutor& ex);
    template<class ExecutionContext>
      socket_type_for<typename ExecutionContext::executor_type>
        accept(ExecutionContext& ctx);
    // ...
  };

The second of these overloads shall not participate in overload resolution unless is_convertible<ExecutionContext&, execution_context&>::value is true, and is_constructible<executor_type, typename ExecutionContext::executor_type>::value is true.

Modification of connect and async_connect functions

An Executor template parameter is added as required to the connect and async_connect functions. For example:

  template<class Protocol, class Executor, class EndpointSequence>
    typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                        const EndpointSequence& endpoints);

Modification of basic_socket_streambuf

The basic_socket_streambuf default constructor is modified so that it shall not participate in overload resolution unless is_constructible<executor_type, io_context::executor_type>::value is true.

Modification of basic_socket_iostream

The basic_socket_iostream specification is modified so that the executor_type type is passed to all uses of basic_socket_streambuf, as required. The basic_socket_iostream default constructor is modified so that it shall not participate in overload resolution unless is_default_constructible<basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_type>>::value is true.

4. Possible implementation approach

This section is intended to provide implementation suggestions only, and is not intended to be prescriptive or exhaustive.

Provision of default implementation

Implementations may use an execution context service as a container for the backend implementation.

  class __socket_backend : public execution_context::service
  {
    // ...
  };

This service would be "used" by an I/O object implementation, and is automatically created on first use:

  template <class Protocol, class Executor>
  class basic_socket : public socket_base
  {
  public:
    using executor_type = Executor;

    // ...

    explicit basic_socket(const executor_type& ex)
    {
      auto& backend = use_service<__socket_backend>(ex.context());
      // ...
    }

    // ...
  };

This allows the I/O object to be used with arbitrary execution contexts.

Native implementations

A native execution context preemptively performs service creation by calling make_service. It can use this opportunity to pass additional constructor arguments that initialise the backend in the native mode, rather than the default:

  class io_context : public execution_context
  {
  public:
    // ...

    io_context()
    {
      make_service<__socket_backend>(*this, __io_context_backend_tag{});
    }

    // ...
  };

Alternatively, implementations may use a class hierarchy of services, and virtual functions, to select the desired behaviour:

  class __socket_backend : public execution_context::service
  {
  public:
    using key_type =  __socket_backend;

    // ...

    virtual void backend_function(/* ... */);

    // ...
  };

  class __io_socket_backend : public __socket_backend
  {
  public:
    // ...

    void backend_function(/* ... */) override;

    // ...
  };

  class io_context : public execution_context
  {
  public:
    // ...

    io_context()
    {
      make_service<__io_socket_backend>(*this);
    }

    // ...
  };

In both approaches, the existing service, with its native backend, is obtained by the I/O object constructor.

Performance impact

Specification of the polymorphic executor as the default I/O executor, while improving usability, has a non-zero impact on performance. This impact can be mitigated by having an I/O object's constructor detect its own well-known native executor types (e.g. by using executor::target_type). With this information, the overhead can then be limited to simple branching, rather than the memory allocations and virtual functions that would likely be required for the type-erased function objects.

Users can avoid this overhead completely by explicitly specifying the I/O executor type:

  io_context my_context;
  basic_stream_socket<tcp, io_context::executor_type> my_socket(my_context);

5. Implementation experience

This design change was implemented in Asio 1.13.0, and was delivered in the Boost 1.70 release as the stable Asio version 1.14.0.

6. Proposed changes to wording

These changes are relative to N4771.

Update the <experimental/netfwd> synopsis [fwd.decl.synop] as follows:

12.1 Header <experimental/netfwd> synopsis [fwd.decl.synop]

namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class execution_context;
  template<class T, class Executor>
    class executor_binder;
  template<class Executor>
    class executor_work_guard;
  class system_executor;
  class executor;
  template<class Executor>
    class strand;

  class io_context;

  template<class Clock> struct wait_traits;
  template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor>
    class basic_waitable_timer;
  using system_timer = basic_waitable_timer<chrono::system_clock>;
  using steady_timer = basic_waitable_timer<chrono::steady_clock>;
  using high_resolution_timer = basic_waitable_timer<chrono::high_resolution_clock>;

  template<class Protocol, class Executor = executor>
    class basic_socket;
  template<class Protocol, class Executor = executor>
    class basic_datagram_socket;
  template<class Protocol, class Executor = executor>
    class basic_stream_socket;
  template<class Protocol, class Executor = executor>
    class basic_socket_acceptor;
  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>, class Executor = executor>
      class basic_socket_streambuf;
  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>, class Executor = executor>
      class basic_socket_iostream;

  namespace ip {

    class address;
    class address_v4;
    class address_v6;
    template<class Address>
      class basic_address_iterator;
    using address_v4_iterator = basic_address_iterator<address_v4>;
    using address_v6_iterator = basic_address_iterator<address_v6>;
    template<class Address>
      class basic_address_range;
    using address_v4_range = basic_address_range<address_v4>;
    using address_v6_range = basic_address_range<address_v6>;
    class network_v4;
    class network_v6;
    template<class InternetProtocol>
      class basic_endpoint;
    template<class InternetProtocol>
      class basic_resolver_entry;
    template<class InternetProtocol>
      class basic_resolver_results;
    template<class InternetProtocol, class Executor = executor>
      class basic_resolver;
    class tcp;
    class udp;

  } // namespace ip
} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

Update the <experimental/timer> synopsis [timer.synop] as follows:

15.1 Header <experimental/timer> synopsis [timer.synop]

[...]

  template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor>
    class basic_waitable_timer;

Update the class template basic_waitable_timer [timer.waitable] as follows:

15.4 Class template basic_­waitable_­timer [timer.waitable]

namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Clock, class WaitTraits = wait_traits<Clock>, class Executor = executor>
  class basic_waitable_timer
  {
  public:
    // types:

    using executor_type = io_­context​::​executor_­typeExecutor;
    using clock_type = Clock;
    using duration = typename clock_type::duration;
    using time_point = typename clock_type::time_point;
    using traits_type = WaitTraits;

    template<class OtherExecutor>
    using rebind_­executor =
      basic_­waitable_­timer<Clock, WaitTraits, OtherExecutor>;

    // [timer.waitable.cons], construct / copy / destroy:

    explicit basic_waitable_timer(io_­context& ctxconst executor_­type& ex);
    template<class ExecutionContext>
      explicit basic_­waitable_­timer(ExecutionContext& ctx);
    basic_waitable_timer(io_­context& ctxconst executor_­type& ex, const time_point& t);
    template<class ExecutionContext>
      basic_­waitable_­timer(ExecutionContext& ctx, const time_­point& t);
    basic_waitable_timer(io_­context& ctxconst executor_­type& ex, const duration& d);
    template<class ExecutionContext>
      basic_­waitable_­timer(ExecutionContext& ctx, const duration& d);
    basic_waitable_timer(const basic_waitable_timer&) = delete;
    basic_waitable_timer(basic_waitable_timer&& rhs);

Update the basic_waitable_timer constructors [timer.waitable.cons] as follows:

15.4.1 basic_­waitable_­timer constructors [timer.waitable.cons]

explicit basic_waitable_timer(io_­context& ctxconst executor_­type& ex);
Effects:Equivalent to basic_­waitable_­timer(ctxex, time_­point()).
template<class ExecutionContext>
  explicit basic_­waitable_­timer(ExecutionContext& ctx);
Effects:Equivalent to basic_­waitable_­timer(ctx.get_­executor()).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_waitable_timer(io_­context& ctxconst executor_­type& ex, const time_point& t);
Postconditions:
  • get_­executor() == ctx.get_executor()ex.
  • expiry() == t.
template<class ExecutionContext>
  basic_­waitable_­timer(ExecutionContext& ctx, const time_­point& t);
Effects:Equivalent to basic_­waitable_­timer(ctx.get_­executor(), t).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_waitable_timer(io_­context& ctxconst executor_­type& ex, const duration& d);
Effects:Sets the expiry time as if by calling expires_­after(d).
Postconditions:get_­executor() == ctx.get_executor()ex.
template<class ExecutionContext>
  basic_­waitable_­timer(ExecutionContext& ctx, const duration& d);
Effects:Equivalent to basic_­waitable_­timer(ctx.get_­executor(), d).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.

Update the <experimental/socket> synopsis [socket.synop] as follows:

18.1 Header <experimental/socket> synopsis [socket.synop]

namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  enum class socket_errc {
    already_open = an implementation-defined non-zero value,
    not_found = an implementation-defined non-zero value
  };

  const error_category& socket_category() noexcept;

  error_code make_error_code(socket_errc e) noexcept;
  error_condition make_error_condition(socket_errc e) noexcept;

  // Sockets:

  class socket_base;

  template<class Protocol, class Executor = executor>
    class basic_socket;

  template<class Protocol, class Executor = executor>
    class basic_datagram_socket;

  template<class Protocol, class Executor = executor>
    class basic_stream_socket;

  template<class Protocol, class Executor = executor>
    class basic_socket_acceptor;

  // [socket.iostreams], Socket streams:

  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>, class Executor = executor>
      class basic_socket_streambuf;

  template<class Protocol, class Clock = chrono::steady_clock,
    class WaitTraits = wait_traits<Clock>, class Executor = executor>
      class basic_socket_iostream;

  // [socket.algo.connect], synchronous connect operations:

  template<class Protocol, class Executor, class EndpointSequence>
    typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                        const EndpointSequence& endpoints);
  template<class Protocol, class Executor, class EndpointSequence>
    typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                        const EndpointSequence& endpoints,
                                        error_code& ec);
  template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition>
    typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                        const EndpointSequence& endpoints,
                                        ConnectCondition c);
  template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition>
    typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                        const EndpointSequence& endpoints,
                                        ConnectCondition c,
                                        error_code& ec);

  template<class Protocol, class Executor, class InputIterator>
    InputIterator connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last);
  template<class Protocol, class Executor, class InputIterator>
    InputIterator connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          error_code& ec);
  template<class Protocol, class Executor, class InputIterator, class ConnectCondition>
    InputIterator connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          ConnectCondition c);
  template<class Protocol, class Executor, class InputIterator, class ConnectCondition>
    InputIterator connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          ConnectCondition c,
                          error_code& ec);

  // [socket.algo.async.connect], asynchronous connect operations:

  template<class Protocol, class Executor, class EndpointSequence, class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          const EndpointSequence& endpoints,
                          CompletionToken&& token);
  template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition,
           class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          const EndpointSequence& endpoints,
                          ConnectCondition c,
                          CompletionToken&& token);

  template<class Protocol, class Executor, class InputIterator, class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          CompletionToken&& token);
  template<class Protocol, class Executor, class InputIterator, class ConnectCondition,
           class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          ConnectCondition c,
                          CompletionToken&& token);

} // inline namespace v1
} // namespace net
} // namespace experimental

  template<> struct is_error_code_enum<experimental::net::v1::socket_errc>
    : public true_type {};

} // namespace std

Update the AcceptableProtocol requirements [socket.reqmts.acceptableprotocol] as follows:

18.2.7 Acceptable protocol requirements [socket.reqmts.acceptableprotocol]

A type X meets the AcceptableProtocol requirements if it satisfies the requirements of Protocol ([socket.reqmts.protocol]) as well as the additional requirements listed below.
In the table below, E is a type that satisfies the Executor requirements ([async.reqmts.executor]).
Table 26: AcceptableProtocol requirements
expressionreturn typeassertion/note pre/post-conditions
X​::​socket
A type that satisfies the requirements of Destructible (C++2014[destructible]) and MoveConstructible (C++2014[moveconstructible]), and that is publicly and unambiguously derived from basic_­socket<X>.
X​::​socket_­for<E>A type that satisfies the requirements of Destructible (C++2014[destructible]) and MoveConstructible (C++2014[moveconstructible]), and that is publicly and unambiguously derived from basic_­socket<X, E>.

Update the class socket_base [socket.base] as follows:

18.4 Class socket_­base [socket.base]

namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  class socket_base
  {
  public:
    class broadcast;
    class debug;
    class do_not_route;
    class keep_alive;
    class linger;
    class out_of_band_inline;
    class receive_buffer_size;
    class receive_low_watermark;
    class reuse_address;
    class send_buffer_size;
    class send_low_watermark;

    using shutdown_type = T1;
    static constexpr shutdown_type shutdown_receive;
    static constexpr shutdown_type shutdown_send;
    static constexpr shutdown_type shutdown_both;

    using wait_type = T2;
    static constexpr wait_type wait_read;
    static constexpr wait_type wait_write;
    static constexpr wait_type wait_error;

    using message_flags = T3;
    static constexpr message_flags message_peek;
    static constexpr message_flags message_out_of_band;
    static constexpr message_flags message_do_not_route;

    static const int max_listen_connections;

  protected:
    socket_base();
    ~socket_base();
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
socket_­base defines several member types:

Update the class template basic_socket [socket.basic] as follows:

18.6 Class template basic_­socket [socket.basic]

Class template basic_­socket<Protocol, Executor> is used as the base class for the basic_­datagram_­socket<Protocol, Executor> and basic_­stream_­socket<Protocol, Executor> class templates. It provides functionality that is common to both types of socket.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Protocol, class Executor>
  class basic_socket : public socket_base
  {
  public:
    // types:

    using executor_type = io_­context​::​executor_­typeExecutor;
    using native_handle_type = implementation-defined; // see [socket.reqmts.native]
    using protocol_type = Protocol;
    using endpoint_type = typename protocol_type::endpoint;

    template<class OtherExecutor>
    using rebind_­executor =
      basic_­socket<Protocol, OtherExecutor>;

    // [socket.basic.ops], basic_socket operations:

    executor_type get_executor() noexcept;

    native_handle_type native_handle(); // see [socket.reqmts.native]

    void open(const protocol_type& protocol = protocol_type());
    void open(const protocol_type& protocol, error_code& ec);

    void assign(const protocol_type& protocol,
                const native_handle_type& native_socket); // see [socket.reqmts.native]
    void assign(const protocol_type& protocol,
                const native_handle_type& native_socket,
                error_code& ec); // see [socket.reqmts.native]

    native_handle_type release(); // see [socket.reqmts.native]
    native_handle_type release(error_code& ec); // see [socket.reqmts.native]

    bool is_open() const noexcept;

    void close();
    void close(error_code& ec);

    void cancel();
    void cancel(error_code& ec);

    template<class SettableSocketOption>
      void set_option(const SettableSocketOption& option);
    template<class SettableSocketOption>
      void set_option(const SettableSocketOption& option, error_code& ec);

    template<class GettableSocketOption>
      void get_option(GettableSocketOption& option) const;
    template<class GettableSocketOption>
      void get_option(GettableSocketOption& option, error_code& ec) const;

    template<class IoControlCommand>
      void io_control(IoControlCommand& command);
    template<class IoControlCommand>
      void io_control(IoControlCommand& command, error_code& ec);

    void non_blocking(bool mode);
    void non_blocking(bool mode, error_code& ec);
    bool non_blocking() const;

    void native_non_blocking(bool mode);
    void native_non_blocking(bool mode, error_code& ec);
    bool native_non_blocking() const;

    bool at_mark() const;
    bool at_mark(error_code& ec) const;

    size_t available() const;
    size_t available(error_code& ec) const;

    void bind(const endpoint_type& endpoint);
    void bind(const endpoint_type& endpoint, error_code& ec);

    void shutdown(shutdown_type what);
    void shutdown(shutdown_type what, error_code& ec);

    endpoint_type local_endpoint() const;
    endpoint_type local_endpoint(error_code& ec) const;

    endpoint_type remote_endpoint() const;
    endpoint_type remote_endpoint(error_code& ec) const;

    void connect(const endpoint_type& endpoint);
    void connect(const endpoint_type& endpoint, error_code& ec);

    template<class CompletionToken>
      DEDUCED async_connect(const endpoint_type& endpoint,
                            CompletionToken&& token);

    void wait(wait_type w);
    void wait(wait_type w, error_code& ec);

    template<class CompletionToken>
      DEDUCED async_wait(wait_type w, CompletionToken&& token);

  protected:
    // [socket.basic.cons], construct / copy / destroy:

    explicit basic_socket(io_­context& ctxconst executor_­type& ex);
    template<class ExecutionContext>
      explicit basic_­socket(ExecutionContext& ctx);
    basic_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
    template<class ExecutionContext>
      basic_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
    basic_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
    template<class ExecutionContext>
      basic_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
    basic_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
                 const native_handle_type& native_socket); // see [socket.reqmts.native]
    template<class ExecutionContext>
      basic_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
                   const native_­handle_­type& native_­socket); // see [socket.reqmts.native]
    basic_socket(const basic_socket&) = delete;
    basic_socket(basic_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs);

    ~basic_socket();

    basic_socket& operator=(const basic_socket&) = delete;
    basic_socket& operator=(basic_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs);

  private:
    protocol_type protocol_; // exposition only
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

Update the basic_socket constructors [socket.basic.cons] as follows:

18.6.1 basic_­socket constructors [socket.basic.cons]

explicit basic_socket(io_­context& ctxconst executor_­type& ex);
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == false.
template<class ExecutionContext>
  explicit basic_­socket(ExecutionContext& ctx);
Effects:Equivalent to basic_­socket(ctx.get_­executor()).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
Effects:Opens this socket as if by calling open(protocol).
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • protocol_­ == protocol.
template<class ExecutionContext>
  explicit basic_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
Effects:Equivalent to basic_­socket(ctx.get_­executor(), protocol).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
Effects:Opens and binds this socket as if by calling:
open(endpoint.protocol());
bind(endpoint);
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • protocol_­ == endpoint.protocol().
template<class ExecutionContext>
  explicit basic_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
Effects:Equivalent to basic_­socket(ctx.get_­executor(), endpoint).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
             const native_handle_type& native_socket);
Requires:native_­socket is a native handle to an open socket.
Effects:Assigns the existing native socket into this socket as if by calling assign(protocol, native_­socket).
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • protocol_­ == protocol.
template<class ExecutionContext>
  basic_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
               const native_­handle_­type& native_­socket);
Effects:Equivalent to basic_­socket(ctx.get_­executor(), protocol, native_­socket).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket(basic_socket&& rhs);
Effects:Move constructs an object of class basic_­socket<Protocol, Executor> that refers to the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the constructor invocation.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the constructor invocation.
  • native_­handle() returns the prior value of rhs.native_­handle().
  • protocol_­ is the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
template<class OtherProtocol, class OtherExecutor>
  basic_socket(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Move constructs an object of class basic_­socket<Protocol, Executor> that refers to the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the constructor invocation.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the constructor invocation.
  • native_­handle() returns the prior value of rhs.native_­handle().
  • protocol_­ is the result of converting the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
Remarks:This constructor shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the basic_socket assignment operators [socket.basic.assign] as follows:

18.6.3 basic_­socket assignment [socket.basic.assign]

basic_socket& operator=(basic_socket&& rhs);
Effects:If is_­open() is true, cancels all outstanding asynchronous operations associated with this socket. Completion handlers for canceled operations are passed an error code ec such that ec == errc​::​operation_­canceled yields true. Disables the linger socket option to prevent the assignment from blocking, and releases socket resources as if by POSIX close(native_­handle()). Moves into *this the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the assignment.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the assignment.
  • protocol_­ is the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
Returns:*this.
template<class OtherProtocol, class OtherExecutor>
  basic_socket& operator=(basic_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:If is_­open() is true, cancels all outstanding asynchronous operations associated with this socket. Completion handlers for canceled operations are passed an error code ec such that ec == errc​::​operation_­canceled yields true. Disables the linger socket option to prevent the assignment from blocking, and releases socket resources as if by POSIX close(native_­handle()). Moves into *this the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the assignment.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the assignment.
  • protocol_­ is the result of converting the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
Returns:*this.
Remarks:This assignment operator shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the class template basic_datagram_socket [socket.basic] as follows:

18.7 Class template basic_­datagram_­socket [socket.dgram]

The class template basic_­datagram_­socket<Protocol, Executor> is used to send and receive discrete messages of fixed maximum length.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Protocol, class Executor>
  class basic_datagram_socket : public basic_socket<Protocol, Executor>
  {
  public:
    // types:

    using executor_­type = Executor;
    using native_handle_type = implementation-defined; // see [socket.reqmts.native]
    using protocol_type = Protocol;
    using endpoint_type = typename protocol_type::endpoint;

    template<class OtherExecutor>
    using rebind_­executor =
      basic_­datagram_­socket<Protocol, OtherExecutor>;

    // [socket.dgram.cons], construct / copy / destroy:

    explicit basic_datagram_socket(io_­context& ctxconst executor_­type& ex);
    template<class ExecutionContext>
      explicit basic_­datagram_­socket(ExecutionContext& ctx);
    basic_datagram_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
    template<class ExecutionContext>
      basic_­datagram_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
    basic_datagram_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
    template<class ExecutionContext>
      basic_­datagram_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
    basic_datagram_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
                          const native_handle_type& native_socket);
    template<class ExecutionContext>
      basic_­datagram_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
                            const native_­handle_­type& native_­socket);
    basic_datagram_socket(const basic_datagram_socket&) = delete;
    basic_datagram_socket(basic_datagram_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_datagram_socket(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);

    ~basic_datagram_socket();

    basic_datagram_socket& operator=(const basic_datagram_socket&) = delete;
    basic_datagram_socket& operator=(basic_datagram_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_datagram_socket& operator=(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);

[...]

If native_­handle_­type and basic_­socket<Protocol, Executor>​::​native_­handle_­type are both defined then they name the same type.

Update the basic_datagram_socket constructors [socket.dgram.cons] as follows:

18.7.1 basic_­datagram_­socket constructors [socket.dgram.cons]

explicit basic_datagram_socket(io_­context& ctxconst executor_­type& ex);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctxex).
template<class ExecutionContext>
  explicit basic_­datagram_­socket(ExecutionContext& ctx);
Effects:Equivalent to basic_­datagram_­socket(ctx.get_­executor()).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_datagram_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, protocol).
template<class ExecutionContext>
  explicit basic_­datagram_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
Effects:Equivalent to basic_­datagram_­socket(ctx.get_­executor(), protocol).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_datagram_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, endpoint).
template<class ExecutionContext>
  explicit basic_­datagram_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
Effects:Equivalent to basic_­datagram_­socket(ctx.get_­executor(), endpoint).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_datagram_socket(io_context& ctx, const protocol_type& protocol,
                      const native_handle_type& native_socket);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, protocol, native_­socket).
template<class ExecutionContext>
  basic_­datagram_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
                        const native_­handle_­type& native_­socket);
Effects:Equivalent to basic_­datagram_­socket(ctx.get_­executor(), protocol, native_­socket).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_datagram_socket(basic_datagram_socket&& rhs);
Effects:Move constructs an object of class basic_­datagram_­socket<Protocol, Executor>, initializing the base class with basic_­socket<Protocol, Executor>(std​::​move(rhs)).
template<class OtherProtocol, class OtherExecutor>
  basic_datagram_socket(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Move constructs an object of class basic_­datagram_­socket<Protocol, Executor>, initializing the base class with basic_­socket<Protocol, Executor>(std​::​move(rhs)).
Remarks:This constructor shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the basic_datagram_socket assignment operators [socket.dgram.assign] as follows:

18.7.2 basic_­datagram_­socket assignment [socket.dgram.assign]

basic_datagram_socket& operator=(basic_datagram_socket&& rhs);
Effects:Equivalent to basic_­socket<Protocol, Executor>​::​operator=(std​::​move(rhs)).
Returns:*this.
template<class OtherProtocol, class OtherExecutor>
  basic_datagram_socket& operator=(basic_datagram_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Equivalent to basic_­socket<Protocol, Executor>​::​operator=(std​::​move(rhs)).
Returns:*this.
Remarks:This assignment operator not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the class template basic_stream_socket [socket.stream] as follows:

18.8 Class template basic_­stream_­socket [socket.stream]

The class template basic_­stream_­socket<Protocol, Executor> is used to exchange data with a peer over a sequenced, reliable, bidirectional, connection-mode byte stream.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Protocol, class Executor>
  class basic_stream_socket : public basic_socket<Protocol, Executor>
  {
  public:
    // types:

    using executor_­type = Executor;
    using native_handle_type = implementation-defined; // see [socket.reqmts.native]
    using protocol_type = Protocol;
    using endpoint_type = typename protocol_type::endpoint;

    template<class OtherExecutor>
    using rebind_­executor =
      basic_­stream_­socket<Protocol, OtherExecutor>;

    // [socket.stream.cons], construct / copy / destroy:

    explicit basic_stream_socket(io_­context& ctxconst executor_­type& ex);
    template<class ExecutionContext>
      explicit basic_­stream_­socket(ExecutionContext& ctx);
    basic_stream_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
    template<class ExecutionContext>
      basic_­stream_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
    basic_stream_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
    template<class ExecutionContext>
      basic_­stream_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
    basic_stream_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
                 const native_handle_type& native_socket);
    template<class ExecutionContext>
      basic_­stream_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
                   const native_­handle_­type& native_­socket);
    basic_stream_socket(const basic_stream_socket&) = delete;
    basic_stream_socket(basic_stream_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_stream_socket(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);

    ~basic_stream_socket();

    basic_stream_socket& operator=(const basic_stream_socket&) = delete;
    basic_stream_socket& operator=(basic_stream_socket&& rhs);
    template<class OtherProtocol, class OtherExecutor>
      basic_stream_socket& operator=(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);

[...]

If native_­handle_­type and basic_­socket<Protocol, Executor>​::​native_­handle_­type are both defined then they name the same type.

Update the basic_stream_socket constructors [socket.stream.cons] as follows:

18.8.1 basic_­stream_­socket constructors [socket.stream.cons]

explicit basic_stream_socket(io_­context& ctxconst executor_­type& ex);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx).
template<class ExecutionContext>
  explicit basic_­stream_­socket(ExecutionContext& ctx);
Effects:Equivalent to basic_­stream_­socket(ctx.get_­executor()).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_stream_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, protocol).
template<class ExecutionContext>
  explicit basic_­stream_­socket(ExecutionContext& ctx, const protocol_­type& protocol);
Effects:Equivalent to basic_­stream_­socket(ctx.get_­executor(), protocol).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_stream_socket(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, endpoint).
template<class ExecutionContext>
  explicit basic_­stream_­socket(ExecutionContext& ctx, const endpoint_­type& endpoint);
Effects:Equivalent to basic_­stream_­socket(ctx.get_­executor(), endpoint).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_stream_socket(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
                    const native_handle_type& native_socket);
Effects:Initializes the base class with basic_­socket<Protocol, Executor>(ctx, protocol, native_­socket).
template<class ExecutionContext>
  basic_­stream_­socket(ExecutionContext& ctx, const protocol_­type& protocol,
                      const native_­handle_­type& native_­socket);
Effects:Equivalent to basic_­stream_­socket(ctx.get_­executor(), protocol, native_­socket).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_stream_socket(basic_stream_socket&& rhs);
Effects:Move constructs an object of class basic_­stream_­socket<Protocol, Executor>, initializing the base class with basic_­socket<Protocol, Executor>(std​::​move(rhs)).
template<class OtherProtocol, class OtherExecutor>
  basic_stream_socket(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Move constructs an object of class basic_­stream_­socket<Protocol, Executor>, initializing the base class with basic_­socket<Protocol, Executor>(std​::​move(rhs)).
Remarks:This constructor shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the basic_stream_socket assignment operators [socket.stream.assign] as follows:

18.8.2 basic_­stream_­socket assignment [socket.stream.assign]

basic_stream_socket& operator=(basic_stream_socket&& rhs);
Effects:Equivalent to basic_­socket<Protocol, Executor>​::​operator=(std​::​move(rhs)).
Returns:*this.
template<class OtherProtocol, class OtherExecutor>
  basic_stream_socket& operator=(basic_stream_socket<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Equivalent to basic_­socket<Protocol, Executor>​::​operator=(std​::​move(rhs)).
Returns:*this.
Remarks:This assignment operator shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the class template basic_socket_acceptor [socket.acceptor] as follows:

18.9 Class template basic_­socket_­acceptor [socket.acceptor]

An object of class template basic_­socket_­acceptor<AcceptableProtocol, Executor> is used to listen for, and queue, incoming socket connections. Socket objects that represent the incoming connections are dequeued by calling accept or async_­accept.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class AcceptableProtocol, class Executor>
  class basic_socket_acceptor : public socket_base
  {
  public:
    // types:

    using executor_type = io_­context​::​executor_­typeExecutor;
    using native_handle_type = implementation-defined; // see [socket.reqmts.native]
    using protocol_type = AcceptableProtocol;
    using endpoint_type = typename protocol_type::endpoint;
    using socket_type = typename protocol_type::socket;
template<class OtherExecutor> using socket_­type_­for = typename protocol_­type​::​socket_­for<OtherExecutor>;
template<class OtherExecutor> using rebind_­executor = basic_­socket_­acceptor<AcceptableProtocol, OtherExecutor>; // [socket.acceptor.cons], construct / copy / destroy: explicit basic_socket_acceptor(io_­context& ctxconst executor_­type& ex); template<class ExecutionContext> explicit basic_­socket_­acceptor(ExecutionContext& ctx); basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol); template<class ExecutionContext> basic_­socket_­acceptor(ExecutionContext& ctx, const protocol_­type& protocol); basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint, bool reuse_addr = true); template<class ExecutionContext> basic_­socket_­acceptor(ExecutionContext& ctx, const endpoint_­type& endpoint, bool reuse_­addr = true); basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol, const native_handle_type& native_socket); template<class ExecutionContext> basic_­socket_­acceptor(ExecutionContext& ctx, const protocol_­type& protocol, const native_­handle_­type& native_­socket); basic_socket_acceptor(const basic_socket_acceptor&) = delete; basic_socket_acceptor(basic_socket_acceptor&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs); ~basic_socket_acceptor(); basic_socket_acceptor& operator=(const basic_socket_acceptor&) = delete; basic_socket_acceptor& operator=(basic_socket_acceptor&& rhs); template<class OtherProtocol, class OtherExecutor> basic_socket_acceptor& operator=(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs); // [socket.acceptor.ops], basic_socket_acceptor operations: executor_type get_executor() noexcept; native_handle_type native_handle(); // see [socket.reqmts.native] void open(const protocol_type& protocol = protocol_type()); void open(const protocol_type& protocol, error_code& ec); void assign(const protocol_type& protocol, const native_handle_type& native_acceptor); // see [socket.reqmts.native] void assign(const protocol_type& protocol, const native_handle_type& native_acceptor, error_code& ec); // see [socket.reqmts.native] native_handle_type release(); // see [socket.reqmts.native] native_handle_type release(error_code& ec); // see [socket.reqmts.native] bool is_open() const noexcept; void close(); void close(error_code& ec); void cancel(); void cancel(error_code& ec); template<class SettableSocketOption> void set_option(const SettableSocketOption& option); template<class SettableSocketOption> void set_option(const SettableSocketOption& option, error_code& ec); template<class GettableSocketOption> void get_option(GettableSocketOption& option) const; template<class GettableSocketOption> void get_option(GettableSocketOption& option, error_code& ec) const; template<class IoControlCommand> void io_control(IoControlCommand& command); template<class IoControlCommand> void io_control(IoControlCommand& command, error_code& ec); void non_blocking(bool mode); void non_blocking(bool mode, error_code& ec); bool non_blocking() const; void native_non_blocking(bool mode); void native_non_blocking(bool mode, error_code& ec); bool native_non_blocking() const; void bind(const endpoint_type& endpoint); void bind(const endpoint_type& endpoint, error_code& ec); void listen(int backlog = max_listen_connections); void listen(int backlog, error_code& ec); endpoint_type local_endpoint() const; endpoint_type local_endpoint(error_code& ec) const; void enable_connection_aborted(bool mode); bool enable_connection_aborted() const; socket_type accept(); socket_type accept(error_code& ec); socket_­type accept(io_­context& ctx); socket_­type accept(io_­context& ctx, error_­code& ec);
template<class OtherExecutor> socket_­type_­for<OtherExecutor> accept(const OtherExecutor& ex); template<class OtherExecutor> socket_­type_­for<OtherExecutor> accept(const OtherExecutor& ex, error_­code& ec); template<class ExecutionContext> socket_­type_­for<typename ExecutionContext​::​executor_­type> accept(ExecutionContext& ctx); template<class ExecutionContext> socket_­type_­for<typename ExecutionContext​::​executor_­type> accept(ExecutionContext& ctx, error_­code& ec);
template<class CompletionToken> DEDUCED async_accept(CompletionToken&& token); template<class CompletionToken> DEDUCED async_­accept(io_­context& ctx, CompletionToken&& token); template<class OtherExecutor, class CompletionToken> DEDUCED async_­accept(const OtherExecutor& ex, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED async_­accept(ExecutionContext& ctx, CompletionToken&& token); socket_type accept(endpoint_type& endpoint); socket_type accept(endpoint_type& endpoint, error_code& ec); socket_­type accept(io_­context& ctx, endpoint_­type& endpoint); socket_­type accept(io_­context& ctx, endpoint_­type& endpoint, error_­code& ec);
template<class OtherExecutor> socket_­type_­for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_­type& endpoint); template<class OtherExecutor> socket_­type_­for<OtherExecutor> accept(const OtherExecutor& ex, endpoint_­type& endpoint, error_­code& ec); template<class ExecutionContext> socket_­type_­for<typename ExecutionContext​::​executor_­type> accept(ExecutionContext& ctx, endpoint_­type& endpoint); template<class ExecutionContext> socket_­type_­for<typename ExecutionContext​::​executor_­type> accept(ExecutionContext& ctx, endpoint_­type& endpoint, error_­code& ec);
template<class CompletionToken> DEDUCED async_accept(endpoint_type& endpoint, CompletionToken&& token); template<class CompletionToken> DEDUCED async_­accept(io_­context& ctx, endpoint_­type& endpoint, CompletionToken&& token); template<class OtherExecutor, class CompletionToken> DEDUCED async_­accept(const OtherExecutor& ex, endpoint_­type& endpoint, CompletionToken&& token); template<class ExecutionContext, class CompletionToken> DEDUCED async_­accept(ExecutionContext& ctx, endpoint_­type& endpoint, CompletionToken&& token);

Update the basic_socket_acceptor constructors [socket.stream.cons] as follows:

18.9.1 basic_­socket_­acceptor constructors [socket.acceptor.cons]

explicit basic_socket_acceptor(io_­context& ctxconst executor_­type& ex);
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == false.
template<class ExecutionContext>
  explicit basic_­socket_­acceptor(ExecutionContext& ctx);
Effects:Equivalent to basic_­socket_­acceptor(ctx.get_­executor()).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol);
Effects:Opens this acceptor as if by calling open(protocol).
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • enable_­connection_­aborted() == false.
  • protocol_­ == protocol.
template<class ExecutionContext>
  explicit basic_­socket_­acceptor(ExecutionContext& ctx, const protocol_­type& protocol);
Effects:Equivalent to basic_­socket_­acceptor(ctx.get_­executor(), protocol).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const endpoint_type& endpoint,
                      bool reuse_addr = true);
Effects:Opens and binds this acceptor as if by calling:
open(endpoint.protocol());
if (reuse_addr)
  set_option(reuse_address(true));
bind(endpoint);
listen();
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • enable_­connection_­aborted() == false.
  • protocol_­ == endpoint.protocol().
template<class ExecutionContext>
  explicit basic_­socket_­acceptor(ExecutionContext& ctx, const endpoint_­type& endpoint,
                                 bool reuse_­addr = true);
Effects:Equivalent to basic_­socket_­acceptor(ctx.get_­executor(), endpoint, reuse_­addr).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket_acceptor(io_­context& ctxconst executor_­type& ex, const protocol_type& protocol,
                      const native_handle_type& native_acceptor);
Requires:native_­acceptor is a native handle to an open acceptor.
Effects:Assigns the existing native acceptor into this acceptor as if by calling assign(protocol, native_­acceptor).
Postconditions:
  • get_­executor() == ctx.get_­executor()ex.
  • is_­open() == true.
  • non_­blocking() == false.
  • enable_­connection_­aborted() == false.
  • protocol_­ == protocol.
template<class ExecutionContext>
  basic_­socket_­acceptor(ExecutionContext& ctx, const protocol_­type& protocol,
                        const native_­handle_­type& native_­socket);
Effects:Equivalent to basic_­socket_­acceptor(ctx.get_­executor(), protocol, native_­socket).
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_socket_acceptor(basic_socket_acceptor&& rhs);
Effects:Move constructs an object of class basic_­socket_­acceptor<AcceptableProtocol, Executor> that refers to the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the constructor invocation.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the constructor invocation.
  • enable_­connection_­aborted() returns the same value as rhs.enable_­connection_­aborted() prior to the constructor invocation.
  • native_­handle() returns the same value as rhs.native_­handle() prior to the constructor invocation.
  • protocol_­ is equal to the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
template<class OtherProtocol, class OtherExecutor>
  basic_socket_acceptor(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:Move constructs an object of class basic_­socket_­acceptor<AcceptableProtocol, Executor> that refers to the state originally represented by rhs.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the constructor invocation.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the constructor invocation.
  • enable_­connection_­aborted() returns the same value as rhs.enable_­connection_­aborted() prior to the constructor invocation.
  • native_­handle() returns the prior value of rhs.native_­handle().
  • protocol_­ is the result of converting the prior value of rhs.protocol_­.
  • rhs.is_­open() == false.
Remarks:This constructor shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the basic_socket_acceptor assignment operators [socket.stream.assign] as follows:

18.9.3 basic_­socket_­acceptor assignment [socket.acceptor.assign]

basic_socket_acceptor& operator=(basic_socket_acceptor&& rhs);
Effects:If is_­open() is true, cancels all outstanding asynchronous operations associated with this acceptor, and releases acceptor resources as if by POSIX close(native_­handle()). Then moves into *this the state originally represented by rhs. Completion handlers for canceled operations are passed an error code ec such that ec == errc​::​operation_­canceled yields true.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the assignment.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the assignment.
  • enable_­connection_­aborted() returns the same value as rhs.enable_­connection_­aborted() prior to the assignment.
  • native_­handle() returns the same value as rhs.native_­handle() prior to the assignment.
  • protocol_­ is the same value as rhs.protocol_­ prior to the assignment.
  • rhs.is_­open() == false.
Returns:*this.
template<class OtherProtocol, class OtherExecutor>
  basic_socket_acceptor& operator=(basic_socket_acceptor<OtherProtocol, OtherExecutor>&& rhs);
Requires:OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.
Effects:If is_­open() is true, cancels all outstanding asynchronous operations associated with this acceptor, and releases acceptor resources as if by POSIX close(native_­handle()). Then moves into *this the state originally represented by rhs. Completion handlers for canceled operations are passed an error code ec such that ec == errc​::​operation_­canceled yields true.
Postconditions:
  • get_­executor() == rhs.get_­executor().
  • is_­open() returns the same value as rhs.is_­open() prior to the assignment.
  • non_­blocking() returns the same value as rhs.non_­blocking() prior to the assignment.
  • enable_­connection_­aborted() returns the same value as rhs.enable_­connection_­aborted() prior to the assignment.
  • native_­handle() returns the same value as rhs.native_­handle() prior to the assignment.
  • protocol_­ is the result of converting the value of rhs.protocol_­ prior to the assignment.
  • rhs.is_­open() == false.
Returns:*this.
Remarks:This assignment operator shall not participate in overload resolution unless OtherProtocol is implicitly convertible to Protocol and OtherExecutor is implicitly convertible to Executor.

Update the basic_socket_acceptor operations [socket.stream.ops] as follows:

18.9.4 basic_­socket_­acceptor operations [socket.acceptor.ops]

[...]

socket_type accept();
socket_type accept(error_code& ec);
Returns:accept(get_­executor().context(), ec).
socket_­type accept(io_­context& ctx);
socket_­type accept(io_­context& ctx, error_­code& ec);
template<class OtherExecutor>
  socket_­type_­for<OtherExecutor>
    accept(const OtherExecutor& ex);
template<class OtherExecutor>
  socket_­type_­for<OtherExecutor>
    accept(const OtherExecutor& ex, error_­code& ec);
Effects:Extracts a socket from the queue of pending connections of the acceptor, as if by POSIX:
native_handle_type h = accept(native_handle(), nullptr, 0);
Returns:On success, socket_­type_­for<OtherExecutor>(ctxex, protocol_­, h). Otherwise socket_­type_­for<OtherExecutor>(ctxex).
template<class ExecutionContext>
  socket_­type_­for<typename ExecutionContext​::​executor_­type>
    accept(ExecutionContext& ctx);
template<class ExecutionContext>
  socket_­type_­for<typename ExecutionContext​::​executor_­type>
    accept(ExecutionContext& ctx, error_­code& ec);
Returns:accept(ctx.get_­executor(), ec).
template<class CompletionToken>
  DEDUCED async_accept(CompletionToken&& token);
Returns:
async_accept(get_executor().context(), forward<CompletionToken>(token))
template<class CompletionToken>
  DEDUCED async_­accept(io_­context& ctx, CompletionToken&& token);
template<class OtherExecutor, class CompletionToken>
  DEDUCED async_­accept(const OtherExecutor& ex, CompletionToken&& token);
Let S be the type socket_­type_­for<OtherExecutor>.
Completion signature:void(error_­code ec, socket_­typeS s).
Effects:Initiates an asynchronous operation to extract a socket from the queue of pending connections of the acceptor, as if by POSIX:
native_handle_type h = accept(native_handle(), nullptr, 0);
On success, s is socket_­type(ctx, protocol_­, h). Otherwise, s is socket_­type(ctx). On success, s is S(ex, protocol_­, h). Otherwise, s is S(ex).
template<class ExecutionContext, class CompletionToken>
  DEDUCED async_­accept(ExecutionContext& ctx, CompletionToken&& token);
Returns:
async_­accept(ctx.get_­executor(), forward<CompletionToken>(token))
socket_type accept(endpoint_type& endpoint);
socket_type accept(endpoint_type& endpoint, error_code& ec);
Returns:accept(get_­executor().context(), endpoint, ec).
socket_­type accept(io_­context& ctx, endpoint_­type& endpoint);
socket_­type accept(io_­context& ctx, endpoint_­type& endpoint,
                   error_­code& ec);
template<class OtherExecutor>
  socket_­type_­for<OtherExecutor>
    accept(const OtherExecutor& ex, endpoint_­type& endpoint);
template<class OtherExecutor>
  socket_­type_­for<OtherExecutor>
    accept(const OtherExecutor& ex, endpoint_­type& endpoint, error_­code& ec);
Effects:Extracts a socket from the queue of pending connections of the acceptor, as if by POSIX:
socklen_t endpoint_len = endpoint.capacity();
native_handle_type h = accept(native_handle(),
                              endpoint.data(),
                              &endpoint_len);
if (h >= 0)
  endpoint.resize(endpoint_len);
Returns:On success, socket_­type(ctxex, protocol_­, h). Otherwise socket_­type(ctxex).
template<class ExecutionContext>
  socket_­type_­for<typename ExecutionContext​::​executor_­type>
    accept(ExecutionContext& ctx, endpoint_­type& endpoint);
template<class ExecutionContext>
  socket_­type_­for<typename ExecutionContext​::​executor_­type>
    accept(ExecutionContext& ctx, endpoint_­type& endpoint, error_­code& ec);
Returns:accept(ctx.get_­executor(), endpoint, ec).
template<class CompletionToken>
  DEDUCED async_accept(endpoint_type& endpoint,
                       CompletionToken&& token);
Returns:
async_accept(get_executor().context(), endpoint, forward<CompletionToken>(token))
template<class CompletionToken>
  DEDUCED async_­accept(io_­context& ctx, endpoint_­type& endpoint,
                       CompletionToken&& token);
template<class OtherExecutor, class CompletionToken>
  DEDUCED async_­accept(const OtherExecutor& ex, endpoint_­type& endpoint,
                       CompletionToken&& token);
Let S be the type socket_­type_­for<OtherExecutor>.
Completion signature:void(error_­code ec, socket_­typeS s).
Effects:Initiates an asynchronous operation to extract a socket from the queue of pending connections of the acceptor, as if by POSIX:
socklen_t endpoint_len = endpoint.capacity();
native_handle_type h = accept(native_handle(),
                              endpoint.data(),
                              &endpoint_len);
if (h >= 0)
  endpoint.resize(endpoint_len);
On success, s is socket_­type(ctx, protocol_­, h). Otherwise, s is socket_­type(ctx). On success, s is S(ex, protocol_­, h). Otherwise, s is S(ex).
template<class ExecutionContext, class CompletionToken>
  DEDUCED async_­accept(ExecutionContext& ctx, endpoint_­type& endpoint,
                       CompletionToken&& token);
Returns:
async_­accept(ctx.get_­executor(), forward<CompletionToken>(token))

Update the class template basic_socket_streambuf [socket.streambuf] as follows:

19.1 Class template basic_­socket_­streambuf [socket.streambuf]

The class basic_­socket_­streambuf<Protocol, Clock, WaitTraits, Executor> associates both the input sequence and the output sequence with a socket. The input and output sequences do not support seeking.
Note
: The input and output sequences are independent as a stream socket provides full duplex I/O. — end note
 ]
Note
: This class is intended for sending and receiving bytes, not characters. Any conversion from characters to bytes, and vice versa, occurs elsewhere. — end note
 ]
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Protocol, class Clock, class WaitTraits, class Executor = executor>
  class basic_socket_streambuf : public basic_streambuf<char>
  {
  public:
    // types:

    using executor_­type = Executor;
    using protocol_type = Protocol;
    using endpoint_type = typename protocol_type::endpoint;
    using clock_type = Clock;
    using time_point = typename clock_type::time_point;
    using duration = typename clock_type::duration;
    using wait_traits_type = WaitTraits;

    // [socket.streambuf.cons], construct / copy / destroy:

    basic_socket_streambuf();
    explicit basic_socket_streambuf(basic_stream_socket<protocol_type, executor_­type> s);
    basic_socket_streambuf(const basic_socket_streambuf&) = delete;
    basic_socket_streambuf(basic_socket_streambuf&& rhs);

    virtual ~basic_socket_streambuf();

    basic_socket_streambuf& operator=(const basic_socket_streambuf&) = delete;
    basic_socket_streambuf& operator=(basic_socket_streambuf&& rhs);

    // [socket.streambuf.members], members:

    basic_socket_streambuf* connect(const endpoint_type& e);
    template<class... Args> basic_socket_streambuf* connect(Args&&... );

    basic_socket_streambuf* close();

    basic_socket<protocol_type, executor_­type>& socket();
    error_code error() const;

    time_point expiry() const;
    void expires_at(const time_point& t);
    void expires_after(const duration& d);

  protected:
    // overridden virtual functions:
    virtual int_type underflow() override;
    virtual int_type pbackfail(int_type c = traits_type::eof()) override;
    virtual int_type overflow(int_type c = traits_type::eof()) override;
    virtual int sync() override;
    virtual streambuf* setbuf(char_type* s, streamsize n) override;

  private:
    basic_stream_socket<protocol_type, executor_­type> socket_; // exposition only
    error_code ec_; // exposition only
    time_point expiry_; // exposition only
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
Instances of class template basic_­socket_­streambuf meet the requirements of Destructible (C++2014[destructible]), MoveConstructible (C++2014[moveconstructible]), and MoveAssignable (C++2014[moveassignable]).

19.1.1 basic_­socket_­streambuf constructors [socket.streambuf.cons]

basic_socket_streambuf();
Effects:Initializes socket_­ with ctx, where ctx is an unspecified object of class io_­context.
Postconditions:expiry() == time_­point​::​max() and !error().
Remarks:This function shall not participate in overload resolution unless is_­constructible<executor_­type, io_­context​::​executor_­type>​::​value is true.
explicit basic_socket_streambuf(basic_stream_socket<protocol_type, executor_­type> s);
Effects:Initializes socket_­ with std​::​move(s).
Postconditions:expiry() == time_­point​::​max() and !error().
basic_socket_streambuf(basic_socket_streambuf&& rhs);
Effects:Move constructs from the rvalue rhs. It is implementation-defined whether the sequence pointers in *this (eback(), gptr(), egptr(), pbase(), pptr(), epptr()) obtain the values which rhs had. Whether they do or not, *this and rhs reference separate buffers (if any at all) after the construction. Additionally *this references the socket which rhs did before the construction, and rhs references no open socket after the construction.
Postconditions:Let rhs_­p refer to the state of rhs just prior to this construction and let rhs_­a refer to the state of rhs just after this construction.
  • is_­open() == rhs_­p.is_­open()
  • rhs_­a.is_­open() == false
  • error() == rhs_­p.error()
  • !rhs_­a.error()
  • expiry() == rhs_­p.expiry()
  • rhs_­a.expiry() == time_­point​::​max()
  • gptr() - eback() == rhs_­p.gptr() - rhs_­p.eback()
  • egptr() - eback() == rhs_­p.egptr() - rhs_­p.eback()
  • ptr() - pbase() == rhs_­p.pptr() - rhs_­p.pbase()
  • pptr() - pbase() == rhs_­p.epptr() - rhs_­p.pbase()
  • if (eback()) eback() != rhs_­a.eback()
  • if (gptr()) gptr() != rhs_­a.gptr()
  • if (egptr()) egptr() != rhs_­a.egptr()
  • if (pbase()) pbase() != rhs_­a.pbase()
  • if (pptr()) pptr() != rhs_­a.pptr()
  • if (epptr()) epptr() != rhs_­a.epptr()
virtual ~basic_socket_streambuf();
Effects:If a put area exists, calls overflow(traits_­type​::​eof()) to flush characters.
Note
: The socket is closed by the basic_­stream_­socket<protocol_­type, executor_­type> destructor. — end note
 ]
basic_socket_streambuf& operator=(basic_socket_streambuf&& rhs);
Effects:Calls this->close() then move assigns from rhs. After the move assignment *this and rhs have the observable state they would have had if *this had been move constructed from rhs.
Returns:*this.

19.1.2 basic_­socket_­streambuf members [socket.streambuf.members]

basic_socket_streambuf* connect(const endpoint_type& e);
Effects:Initializes the basic_­socket_­streambuf as required, closes and re-opens the socket by performing socket_­.close(ec_­) and socket_­.open(e.protocol(), ec_­), then attempts to establish a connection as if by POSIX connect(socket_­.native_­handle(), static_­cast<sockaddr*>(e.data()), e.size()). ec_­ is set to reflect the error code produced by the operation. If the operation does not complete before the absolute timeout specified by expiry_­, the socket is closed and ec_­ is set to errc​::​timed_­out.
Returns:if !ec_­, this; otherwise, a null pointer.
template<class... Args>
  basic_socket_streambuf* connect(Args&&... args);
Effects:Initializes the basic_­socket_­streambuf as required and closes the socket as if by calling socket_­.close(ec_­). Obtains an endpoint sequence endpoints by performing protocol_­type​::​resolver(ctx).resolve(forward<Args>(args)...), where ctx is an unspecified object of class io_­context. For each endpoint e in the sequence, closes and re-opens the socket by performing socket_­.close(ec_­) and socket_­.open(e.protocol(), ec_­), then attempts to establish a connection as if by POSIX connect(socket_­.native_­handle(), static_­cast<sockaddr*>(e.data()), e.size()). ec_­ is set to reflect the error code produced by the operation. If the operation does not complete before the absolute timeout specified by expiry_­, the socket is closed and ec_­ is set to errc​::​timed_­out.
Returns:if !ec_­, this; otherwise, a null pointer.
Remarks:This function shall not participate in overload resolution unless Protocol meets the requirements for an internet protocol ([internet.reqmts.protocol]).
basic_socket_streambuf* close();
Effects:If a put area exists, calls overflow(traits_­type​::​eof()) to flush characters. Regardless of whether the preceding call fails or throws an exception, the function closes the socket as if by basic_­socket<protocol_­type, executor_­type>​::​close(ec_­). If any of the calls made by the function fail, close fails by returning a null pointer. If one of these calls throws an exception, the exception is caught and rethrown after closing the socket.
Returns:this on success, a null pointer otherwise.
Postconditions:is_­open() == false.
basic_socket<protocol_type, executor_­type>& socket();
Returns:socket_­.

Update the class template basic_socket_iostream [socket.iostream] as follows:

19.2 Class template basic_­socket_­iostream [socket.iostream]

The class template basic_­socket_­iostream<Protocol, Clock, WaitTraits, Executor> supports reading and writing on sockets. It uses a basic_­socket_­streambuf<Protocol, Clock, WaitTraits, Executor> object to control the associated sequences.
Note
: This class is intended for sending and receiving bytes, not characters. Any conversion from characters to bytes, and vice versa, occurs elsewhere. — end note
 ]
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {

  template<class Protocol, class Clock, class WaitTraits, class Executor = executor>
  class basic_socket_iostream : public basic_iostream<char>
  {
  public:
    // types:

    using executor_­type = Executor;
    using protocol_type = Protocol;
    using endpoint_type = typename protocol_type::endpoint;
    using clock_type = Clock;
    using time_point = typename clock_type::time_point;
    using duration = typename clock_type::duration;
    using wait_traits_type = WaitTraits;

    // [socket.iostream.cons], construct / copy / destroy:

    basic_socket_iostream();
    explicit basic_socket_iostream(basic_stream_socket<protocol_type, executor_­type> s);
    basic_socket_iostream(const basic_socket_iostream&) = delete;
    basic_socket_iostream(basic_socket_iostream&& rhs);
    template<class... Args>
      explicit basic_socket_iostream(Args&&... args);

    basic_socket_iostream& operator=(const basic_socket_iostream&) = delete;
    basic_socket_iostream& operator=(basic_socket_iostream&& rhs);

    // [socket.iostream.members], members:

    template<class... Args> void connect(Args&&... args);

    void close();

    basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_­type>* rdbuf() const;

    basic_socket<protocol_type, executor_­type>& socket();
    error_code error() const;

    time_point expiry() const;
    void expires_at(const time_point& t);
    void expires_after(const duration& d);

  private:
    basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_­type> sb_; // exposition only
  };

} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std
Instances of class template basic_­socket_­iostream meet the requirements of Destructible (C++2014[destructible]), MoveConstructible (C++2014[moveconstructible]), and MoveAssignable (C++2014[moveassignable]).

19.2.1 basic_­socket_­iostream constructors [socket.iostream.cons]

basic_socket_iostream();
Effects:Initializes the base class as basic_­iostream<char>(&sb_­), value-initializes sb_­, and performs setf(std​::​ios_­base​::​unitbuf).
Remarks:This function shall not participate in overload resolution unless is_­default_­constructible<basic_­socket_­streambuf<protocol_­type, clock_­type, wait_­traits_­type, executor_­type>>​::​value is true.
explicit basic_socket_iostream(basic_stream_socket<protocol_type, executor_­type> s);
Effects:Initializes the base class as basic_­iostream<char>(&sb_­), initializes sb_­ with std​::​move(s), and performs setf(std​::​ios_­base​::​unitbuf).
basic_socket_iostream(basic_socket_iostream&& rhs);
Effects:Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_­socket_­streambuf. Next basic_­iostream<char>​::​set_­rdbuf(&sb_­) is called to install the contained basic_­socket_­streambuf.
template<class... Args>
  explicit basic_socket_iostream(Args&&... args);
Effects:Initializes the base class as basic_­iostream<char>(&sb_­), value-initializes sb_­, and performs setf(std​::​ios_­base​::​unitbuf). Then calls rdbuf()->connect(forward<Args>(args)...). If that function returns a null pointer, calls setstate(failbit).
basic_socket_iostream& operator=(basic_socket_iostream&& rhs);
Effects:Move assigns the base and members of *this from the base and corresponding members of rhs.
Returns:*this.

19.2.2 basic_­socket_­iostream members [socket.iostream.members]

template<class... Args>
  void connect(Args&&... args);
Effects:Calls rdbuf()->connect(forward<Args>(args)...). If that function returns a null pointer, calls setstate(failbit) (which may throw ios_­base​::​failure).
void close();
Effects:Calls rdbuf()->close(). If that function returns a null pointer, calls setstate(failbit) (which may throw ios_­base​::​failure).
basic_socket_streambuf<protocol_type, clock_type, wait_traits_type, executor_­type>* rdbuf() const;
Let SB be the type basic_­socket_­streambuf<protocol_­type, clock_­type, wait_­traits_­type, executor_­type>.
Returns:const_­cast<SB*>(addressof(sb_­)).
basic_socket<protocol_type, executor_­type>& socket();
Returns:rdbuf()->socket().

Update the connect function [socket.algo.connect] as follows:

20.1 Synchronous connect operations [socket.algo.connect]

template<class Protocol, class Executor, class EndpointSequence>
  typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                      const EndpointSequence& endpoints);
template<class Protocol, class Executor, class EndpointSequence>
  typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                      const EndpointSequence& endpoints,
                                      error_code& ec);
Returns:connect(s, endpoints, [](auto, auto){ return true; }, ec).
template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition>
  typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                      const EndpointSequence& endpoints,
                                      ConnectCondition c);
template<class Protocol, class Executor, class EndpointSequence, class ConnectCondition>
  typename Protocol::endpoint connect(basic_socket<Protocol, Executor>& s,
                                      const EndpointSequence& endpoints,
                                      ConnectCondition c, error_code& ec);
Effects:Performs ec.clear(), then finds the first element ep in the sequence endpoints for which:
  • c(ec, ep) yields true;
  • s.close(ec) succeeds;
  • s.open(ep.protocol(), ec) succeeds; and
  • s.connect(ep, ec) succeeds.
Returns:typename Protocol​::​endpoint() if no such element is found, otherwise ep.
Error conditions:
  • socket_­errc​::​not_­found — if endpoints.empty() or if the function object c returned false for all elements in the sequence.
template<class Protocol, class Executor, class InputIterator>
  InputIterator connect(basic_socket<Protocol, Executor>& s,
                        InputIterator first, InputIterator last);
template<class Protocol, class Executor, class InputIterator>
  InputIterator connect(basic_socket<Protocol, Executor>& s,
                        InputIterator first, InputIterator last,
                        error_code& ec);
Returns:connect(s, first, last, [](auto, auto){ return true; }, ec).
template<class Protocol, class Executor, class InputIterator, class ConnectCondition>
  InputIterator connect(basic_socket<Protocol, Executor>& s,
                        InputIterator first, InputIterator last,
                        ConnectCondition c);
template<class Protocol, class Executor, class InputIterator, class ConnectCondition>
  InputIterator connect(basic_socket<Protocol, Executor>& s,
                        InputIterator first, InputIterator last,
                        ConnectCondition c, error_code& ec);
Effects:Performs ec.clear(), then finds the first iterator i in the range [first, ​last) for which:
  • c(ec, *i) yields true;
  • s.close(ec) succeeds;
  • s.open(typename Protocol​::​endpoint(*i).protocol(), ec) succeeds; and
  • s.connect(*i, ec) succeeds.
Returns:last if no such iterator is found, otherwise i.
Error conditions:
  • socket_­errc​::​not_­found — if first == last or if the function object c returned false for all iterators in the range.

Update the async_connect function [socket.algo.async.connect] as follows:

20.2 Asynchronous connect operations [socket.algo.async.connect]

template<class Protocol, class Executor, class EndpointSequence, class CompletionToken>
  DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                        const EndpointSequence& endpoints,
                        CompletionToken&& token);
Returns:
async_connect(s, endpoints, [](auto, auto){ return true; }, forward<CompletionToken>(token))
template<class Protocol, class Executor, class EndpointSequence,
  class ConnectCondition, class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          const EndpointSequence& endpoints,
                          ConnectCondition c,
                          CompletionToken&& token);
A composed asynchronous operation ([async.reqmts.async.composed]).
Completion signature:void(error_­code ec, typename Protocol​::​endpoint ep).
Effects:Performs ec.clear(), then finds the first element ep in the sequence endpoints for which:
  • c(ec, ep) yields true;
  • s.close(ec) succeeds;
  • s.open(ep.protocol(), ec) succeeds; and
  • the asynchronous operation s.async_­connect(ep, unspecified) succeeds.
ec is updated with the result of the s.async_­connect(ep, unspecified) operation, if any. If no such element is found, or if the operation fails with one of the error conditions listed below, ep is set to typename Protocol​::​endpoint().
Note
: The underlying close, open, and async_­connect operations are performed sequentially. — end note
 ]
Error conditions:
  • socket_­errc​::​not_­found — if endpoints.empty() or if the function object c returned false for all elements in the sequence.
  • errc​::​operation_­canceled — if s.is_­open() == false immediately following an async_­connect operation on the underlying socket.
template<class Protocol, class Executor, class InputIterator, class CompletionToken>
  DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                        InputIterator first, InputIterator last,
                        CompletionToken&& token);
Returns:
async_connect(s, first, last, [](auto, auto){ return true; },
              forward<CompletionToken>(token))
template<class Protocol, class Executor, class InputIterator,
  class ConnectCondition, class CompletionToken>
    DEDUCED async_connect(basic_socket<Protocol, Executor>& s,
                          InputIterator first, InputIterator last,
                          ConnectCondition c,
                          CompletionToken&& token);
A composed asynchronous operation ([async.reqmts.async.composed]).
Completion signature:void(error_­code ec, InputIterator i).
Effects:Performs ec.clear(), then finds the first iterator i in the range [first, ​last) for which:
  • c(ec, *i) yields true;
  • s.close(ec) succeeds;
  • s.open(typename Protocol​::​endpoint(*i).protocol(), ec) succeeds; and
  • the asynchronous operation s.async_­connect(*i, unspecified) succeeds.
ec is updated with the result of the s.async_­connect(*i, unspecified) operation, if any. If no such iterator is found, or if the operation fails with one of the error conditions listed below, i is set to last.
Note
: The underlying close, open, and async_­connect operations are performed sequentially. — end note
 ]
Error conditions:
  • socket_­errc​::​not_­found — if first == last or if the function object c returned false for all iterators in the range.
  • errc​::​operation_­canceled — if s.is_­open() == false immediately following an async_­connect operation on the underlying socket.

Update the <experimental/internet> synopsis [internet.synop] as follows:

21.1 Header <experimental/internet> synopsis [internet.synop]

[...]

  template<class InternetProtocol, class Executor = executor>
    class basic_resolver;

Update the class template basic_resolver [internet.resolver] as follows:

21.17 Class template ip​::​basic_­resolver [internet.resolver]

Objects of type basic_­resolver<InternetProtocol, Executor> are used to perform name resolution. Name resolution is the translation of a host name and service name into a sequence of endpoints, or the translation of an endpoint into its corresponding host name and service name.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {
namespace ip {

  template<class InternetProtocol, class Executor = executor>
  class basic_resolver : public resolver_base
  {
  public:
    // types:

    using executor_type = io_­context​::​executor_­typeExecutor;
    using protocol_type = InternetProtocol;
    using endpoint_type = typename InternetProtocol::endpoint;
    using results_type = basic_resolver_results<InternetProtocol>;

    template<class OtherExecutor>
    using rebind_­executor =
      basic_­resolver<InternetProtocol, OtherExecutor>;

    // [internet.resolver.cons], construct / copy / destroy:

    explicit basic_resolver(io_­context& ctxconst executor_­type& ex);
    template<class ExecutionContext> explicit basic_­resolver(ExecutionContext& ctx);
    basic_resolver(const basic_resolver&) = delete;
    basic_resolver(basic_resolver&& rhs) noexcept;

Update the basic_resolver constructors [internet.resolver.cons] as follows:

21.17.1 ip​::​basic_­resolver constructors [internet.resolver.cons]

explicit basic_resolver(io_­context& ctxconst executor_­type& ex);
Postconditions:get_­executor() == ctx.get_executor()ex.
template<class ExecutionContext> explicit basic_­resolver(ExecutionContext& ctx);
Postconditions:get_­executor() == ctx.get_­executor().
Remarks:This function shall not participate in overload resolution unless is_­convertible<ExecutionContext&, execution_­context&>​::​value is true and is_­constructible<executor_­type, typename ExecutionContext​::​executor_­type>​::​value is true.
basic_resolver(basic_resolver&& rhs) noexcept;
Effects:Move constructs an object of class basic_­resolver<InternetProtocol> that refers to the state originally represented by rhs.
Postconditions:get_­executor() == rhs.get_­executor().

Update the ip::tcp synopsis [internet.tcp] as follows:

21.19 Class ip​::​tcp [internet.tcp]

The class tcp encapsulates the types and flags necessary for TCP sockets.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {
namespace ip {

  class tcp
  {
  public:
    // types:
    using endpoint = basic_endpoint<tcp>;
    using resolver = basic_resolver<tcp>;
    using socket = basic_stream_socket<tcp>;
    template<class Executor>
      using socket_­for = basic_­stream_­socket<tcp, Executor>;
    using acceptor = basic_socket_acceptor<tcp>;
    using iostream = basic_socket_iostream<tcp>;
    class no_delay;

    // static members:
    static constexpr tcp v4() noexcept;
    static constexpr tcp v6() noexcept;

    tcp() = delete;
  };

  // [internet.tcp.comparisons], tcp comparisons:
  constexpr bool operator==(const tcp& a, const tcp& b) noexcept;
  constexpr bool operator!=(const tcp& a, const tcp& b) noexcept;

} // namespace ip
} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

Update the ip::udp synopsis [internet.udp] as follows:

21.20 Class ip​::​udp [internet.udp]

The class udp encapsulates the types and flags necessary for UDP sockets.
namespace std {
namespace experimental {
namespace net {
inline namespace v1 {
namespace ip {

  class udp
  {
  public:
    // types:
    using endpoint = basic_endpoint<udp>;
    using resolver = basic_resolver<udp>;
    using socket = basic_datagram_socket<udp>;
    template<class Executor>
      using socket_­for = basic_­datagram_­socket<udp, Executor>;

    // static members:
    static constexpr udp v4() noexcept;
    static constexpr udp v6() noexcept;

    udp() = delete;
  };

  // [internet.udp.comparisons], udp comparisons:
  constexpr bool operator==(const udp& a, const udp& b) noexcept;
  constexpr bool operator!=(const udp& a, const udp& b) noexcept;

} // namespace ip
} // inline namespace v1
} // namespace net
} // namespace experimental
} // namespace std

7. History

Changes in R3:

Note: changed sections in R3 are indicated by a pale blue background with a dark blue line on the right margin.

Changes in R2:

Changes in R1: