tcp_stream


The tcp_stream represents the a duplex network stream for writing and reading data. read, shutdown and cancel are not thread safe. Deconstruction or moving the tcp_stream while in use results in undefined behavior. A user should make sure to ensure to shutdown and wait for any operations to leave the functions before deconstructing the object.

Example
async_function<>
your_class::run_stream(tcp_stream&& _stream)
{
    /* Save locally so doesnt go out of scope when we suspend */
    tcp_stream stream(std::move(_stream));

    auto data_to_send = "hello world";
    std::size_t written = co_await stream.Write(data_to_send);

    if (written == ::strlen(data_to_send)) {

        /* Get 1 byte back as confirmation */
        auto buffer_opt = co_await stream.Read(1);

        /* Read can return less then requested but not more... */
        if (buffer_opt && buffer_opt->size()) {
            std::cout << "Received: " << buffer_opt.at(0) << "\n";
        }
    }

    /* Ensure we shut it down! (flushes all writes and wakes any readers) */
    co_await stream.shut_down();

    /* RAII will clean up for us! */
}

template<MemoryType DataType = std::byte>
class zab::tcp_stream

This class represents the a duplex network stream for writing and reading data.

Template Parameters

DataType – The MemoryType this stream reads and writes.

Public Types

using net_op = network_operation::net_op

Public Functions

inline tcp_stream()

Construct a tcp stream object in an empty state.

Using this object from this constructor is undefined behaviour unless you are writing to it via a swap, a move or move assignment.

inline tcp_stream(engine *_engine, int _fd)

Construct a new tcp stream object associated with a engine and an socket descriptor.

This interface assumes the socket was created in blocking mode.

Parameters
  • _engine – The engine to used.

  • _fd – The socket to used.

tcp_stream(const tcp_stream&) = delete

There is no copy constructor for a tcp_stream.

inline tcp_stream(tcp_stream &&_move)

Construct a new tcp stream object by swapping the resources owned by _move.

Parameters

_move – The tcp stream to move.

inline ~tcp_stream()

Destroy the tcp stream object.

It is far more efficient to manually shutdown, cancel reads and writes and close the socket before the object is deconstructed. Failure to do so will spawn background fibres to do this in the background.

We first try to cancel any pending writes in the background. The clear any errors from the socket.

The deconstruction of net_op_ will cancel any pending reads and close the socket in the background.

inline tcp_stream &operator=(tcp_stream &&tcp_stream)

Move operator for a tcp stream. This will just swap to two streams.

Parameters

tcp_stream – The stream to swap.

Returns

tcp_stream& this.

inline int descriptor() const noexcept

Get the underlying socket descriptor.

Returns

int

inline int last_error() noexcept

Get the last error.

This clears the error.

Returns

int

inline void set_error(int _error) noexcept

Set the error for the stream.

Parameters

_error – The error to set for the stream.

inline auto cancel_read() noexcept

Attempt to cancel the current read operation.

This function only suspends if there is a pending read operation.

@co_return void Resumes after cancelation.

inline auto cancel_write() noexcept

Attempt to cancel the current write operation.

This function only suspends if there is a pending write operation.

@co_return void Resumes after cancelation.

inline auto close() noexcept

Attempt to close the socket.

This function only suspends if there is a socket to close.

@co_return true The socket was successfully closed. @co_return false The socket failed to close.

inline simple_future shutdown() noexcept

Shutdown the stream by cancelling any pending operations and signalling shutdown.

@co_return void on shutdown completion.

inline auto read_some(std::span<DataType> _data, size_t _offset = 0, int _flags = 0) noexcept

Attempt to read up to _data.size() - _offset bytes into the span at the given offset.

If _data.size() - _offset is larger then kMaxRead, then kMaxRead is used as the max.

Parameters
  • _data – The buffer to read into.

  • _offset – The offset from where to start reading in.

  • _flags – Any flags to pass to recv. @co_return int The amount of bytes read or -1 if an error occurred.

inline auto read(std::span<DataType> _data, size_t _offset = 0, int _flags = 0) noexcept

Attempts to _data.size() - _offset bytes into the span at the given offset. Blocks until the amount is read, a signal interupts the call or an error occurs.

If _data.size() - _offset is larger then kMaxRead, then kMaxRead is used as the max.

Parameters
  • _data – The buffer to read into.

  • _offset – The offset from where to start reading in.

  • _flags – Any flags to pass to recv. @co_return long long The amount of bytes read.

inline auto write_some(std::span<const DataType> _data, size_t _offset = 0, int _flags = MSG_NOSIGNAL) noexcept

Attempt to write up to _data.size() - _offset bytes from the span at the given offset.

If _data.size() - _offset is larger then kMaxWrite, then kMaxWrite is used as the max.

Parameters
  • _data – The buffer to write from.

  • _offset – The offset from where to start writing from.

  • _flags – Any flags to pass to send. MSG_NOSIGNAL is always set additionally. @co_return int The amount of bytes written or -1 if an error occurred.

inline auto write(std::span<const DataType> _data, size_t _offset = 0, int _flags = MSG_NOSIGNAL) noexcept

Attempt to write up to _data.size() - _offset bytes from the span at the given offset. Blocks until the amount is written or an error occurs.

If _data.size() - _offset is larger then kMaxWrite, then kMaxWrite is used as the max.

Coroutine chaining can be expensive exspecially if the call to write() is within a hotpath. As such, if the writing is not in a hotpath or are in not performance critical parts of the program write() is encrouaged for readability an maintainabilty. Otherwise authors are encoruged to use write_some and handle their own logic for when the incorrect amount is written.

Parameters
  • _data – The buffer to write from.

  • _offset – The offset from where to start writing from.

  • _flags – Any flags to pass to send. MSG_NOSIGNAL is always set additionaly. @co_return long long The amount of bytes written.

Public Static Attributes

static constexpr auto kMaxWrite = std::numeric_limits<std::uint16_t>::max()

The maximum a write_some operation will write to a stream.

static constexpr auto kMaxRead = std::numeric_limits<int>::max() - 2

The maximum a read_some operation will write to a stream.

Friends

template<MemoryType DT1, MemoryType DT2>
inline friend void swap(tcp_stream<DT1> &_first, tcp_stream<DT2> &_second) noexcept

Swaps two tcp streams.

Template Parameters
  • DT1 – The memory type of the first one.

  • DT2 – The memory type of the second one.

Parameters
  • _first – The first stream.

  • _second – The second stream.

template<MemoryType DT1>
inline friend void swap(tcp_stream<DT1> &_first, tcp_stream<DT1> &_second) noexcept

Swaps two tcp streams.

Template Parameters

DT1 – The memory type of the first one.

Parameters
  • _first – The first stream.

  • _second – The second stream.

template<MemoryType DT1, MemoryType DT2>
inline friend tcp_stream<DT1> rebind(tcp_stream<DT2> &_other)

Rebinds the memory type of the stream via a swap.

Template Parameters
  • DT1 – The memory type of the resulting stream.

  • DT2 – The memory type of the resulting to swap.

Parameters

_other – The stream to rebind.

Returns

tcp_stream<DT1> The newly typed stream.