19 Socket iostreams [socket.iostreams]

19.1 Class template basic_socket_streambuf [socket.streambuf]

The class basic_socket_streambuf<Protocol, Clock, WaitTraits> 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 basic_socket_streambuf : public basic_streambuf<char>
  {
  public:
    // types:

    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> 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>& 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> 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().

explicit basic_socket_streambuf(basic_stream_socket<protocol_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> 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>::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>& socket();

Returns: socket_.

error_code error() const;

Returns: ec_.

time_point expiry() const;

Returns: expiry_.

void expires_at(const time_point& t);

Postconditions: expiry_ == t.

void expires_after(const duration& d);

Effects: Equivalent to expires_at(clock_type::now() + d).

19.1.3 basic_socket_streambuf overridden virtual functions [socket.streambuf.virt]

virtual int_type underflow() override;

Effects: Behaves according to the description of basic_streambuf<char>::underflow(), with the specialization that a sequence of characters is read from the input sequence as if by POSIX recvmsg, and 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: traits_type::to_int_type(*gptr()) to indicate success, and traits_type::eof() to indicate failure.

virtual int_type pbackfail(int_type c = traits_type::eof()) override;

Effects: Puts back the character designated by c to the input sequence, if possible, in one of three ways:

  • If traits_type::eq_int_type(c, traits_type::eof()) returns false, and if the function makes a putback position available, and if traits_type::eq(traits_type::to_char_type(c), gptr()[-1]) returns true, decrements the next pointer for the input sequence, gptr().

    Returns: c.

  • If traits_type::eq_int_type(c, traits_type::eof()) returns false, and if the function makes a putback position available, and if the function is permitted to assign to the putback position, decrements the next pointer for the input sequence, and stores c there.

    Returns: c.

  • If traits_type::eq_int_type(c, traits_type::eof()) returns true, and if either the input sequence has a putback position available or the function makes a putback position available, decrements the next pointer for the input sequence, gptr().

    Returns: traits_type::not_eof(c).

Returns: As specified above, or traits_type::eof() to indicate failure.

Remarks: The function does not put back a character directly to the input sequence. If the function can succeed in more than one of these ways, it is unspecified which way is chosen. The function can alter the number of putback positions available as a result of any call.

virtual int_type overflow(int_type c = traits_type::eof()) override;

Effects: Behaves according to the description of basic_streambuf<char>::overflow(c), except that the behavior of “consuming characters” is performed by output of the characters to the socket as if by one or more calls to POSIX sendmsg, and 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: traits_type::not_eof(c) to indicate success, and traits_type::eof() to indicate failure.

virtual int sync() override;

Effects: If a put area exists, calls overflow(traits_type::eof()) to flush characters.

virtual streambuf* setbuf(char_type* s, streamsize n) override;

Effects: If setbuf(nullptr, 0) is called on a stream before any I/O has occurred on that stream, the stream becomes unbuffered. Otherwise the results are unspecified. “Unbuffered” means that pbase() and pptr() always return null and output to the socket should appear as soon as possible.