Flow 1.0.0
Flow project: Public API.
Public Types | Public Member Functions | Protected Member Functions | Related Functions | List of all members
flow::net_flow::Peer_socket Class Reference

A peer (non-server) socket operating over the Flow network protocol, with optional stream-of-bytes and reliability support. More...

#include <peer_socket.hpp>

Inheritance diagram for flow::net_flow::Peer_socket:
[legend]
Collaboration diagram for flow::net_flow::Peer_socket:
[legend]

Public Types

enum class  State { S_OPEN , S_CLOSED }
 State of a Peer_socket. More...
 
enum class  Open_sub_state { S_CONNECTING , S_CONNECTED , S_DISCONNECTING }
 The sub-state of a Peer_socket when state is State::S_OPEN. More...
 
- Public Types inherited from flow::util::Shared_ptr_alias_holder< boost::shared_ptr< Peer_socket > >
using Ptr = boost::shared_ptr< Peer_socket >
 Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*).
 
using Const_ptr = Const_target_ptr
 Short-hand for ref-counted pointer to immutable values of type Target_type::element_type (a-la T const *).
 

Public Member Functions

 ~Peer_socket () override
 Boring virtual destructor. Note that deletion is to be handled exclusively via shared_ptr, never explicitly.
 
State state (Open_sub_state *open_sub_state=0) const
 Current State of the socket. More...
 
Nodenode () const
 Node that produced this Peer_socket. More...
 
const Remote_endpointremote_endpoint () const
 Intended other side of the connection (regardless of success, failure, or current State). More...
 
flow_port_t local_port () const
 The local Flow-protocol port chosen by the Node (if active or passive open) or user (if passive open) for this side of the connection. More...
 
size_t get_connect_metadata (const boost::asio::mutable_buffer &buffer, Error_code *err_code=0) const
 Obtains the serialized connect metadata, as supplied by the user during the connection handshake. More...
 
template<typename Const_buffer_sequence >
size_t send (const Const_buffer_sequence &data, Error_code *err_code=0)
 Sends (adds to the Send buffer) the given bytes of data up to a maximum internal buffer size; and asynchronously sends them to the other side. More...
 
template<typename Rep , typename Period , typename Const_buffer_sequence >
size_t sync_send (const Const_buffer_sequence &data, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=0)
 Blocking (synchronous) version of send(). More...
 
template<typename Rep , typename Period >
bool sync_send (const boost::asio::null_buffers &, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=0)
 sync_send() operating in null_buffers mode, wherein – if Writable state is reached – the actual data are not moved out of any buffer, leaving that to the caller to do if desired. More...
 
template<typename Const_buffer_sequence >
size_t sync_send (const Const_buffer_sequence &data, Error_code *err_code=0)
 Equivalent to sync_send(data, duration::max(), err_code); i.e., sync_send() with no timeout. More...
 
bool sync_send (const boost::asio::null_buffers &, Error_code *err_code=0)
 Equivalent to sync_send(null_buffers(), duration::max(), err_code); i.e., sync_send(null_buffers) with no timeout. More...
 
template<typename Mutable_buffer_sequence >
size_t receive (const Mutable_buffer_sequence &target, Error_code *err_code=0)
 Receives (consumes from the Receive buffer) bytes of data, up to a given maximum cumulative number of bytes as inferred from size of provided target buffer sequence. More...
 
template<typename Rep , typename Period , typename Mutable_buffer_sequence >
size_t sync_receive (const Mutable_buffer_sequence &target, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=0)
 Blocking (synchronous) version of receive(). More...
 
template<typename Rep , typename Period >
bool sync_receive (const boost::asio::null_buffers &, const boost::chrono::duration< Rep, Period > &max_wait, Error_code *err_code=0)
 sync_receive() operating in null_buffers mode, wherein – if Readable state is reached – the actual data are not moved into any buffer, leaving that to the caller to do if desired. More...
 
template<typename Mutable_buffer_sequence >
size_t sync_receive (const Mutable_buffer_sequence &target, Error_code *err_code=0)
 Equivalent to sync_receive(target, duration::max(), err_code); i.e., sync_receive() with no timeout. More...
 
bool sync_receive (const boost::asio::null_buffers &, Error_code *err_code=0)
 Equivalent to sync_receive(null_buffers(), duration::max(), err_code); i.e., sync_receive(null_buffers) with no timeout. More...
 
void close_abruptly (Error_code *err_code=0)
 Acts as if fatal error error::Code::S_USER_CLOSED_ABRUPTLY has been discovered on the connection. More...
 
bool set_options (const Peer_socket_options &opts, Error_code *err_code=0)
 Dynamically replaces the current options set (options()) with the given options set. More...
 
Peer_socket_options options () const
 Copies this socket's option set and returns that copy. More...
 
Peer_socket_info info () const
 Returns a structure containing the most up-to-date stats about this connection. More...
 
size_t max_block_size () const
 The maximum number of bytes of user data per received or sent packet on this connection. More...
 
Error_code disconnect_cause () const
 The error code that perviously caused state() to become State::S_CLOSED, or success code if state is not CLOSED. More...
 
- Public Member Functions inherited from flow::util::Null_interface
virtual ~Null_interface ()=0
 Boring virtual destructor. More...
 
- Public Member Functions inherited from flow::log::Log_context
 Log_context (Logger *logger=0)
 Constructs Log_context by storing the given pointer to a Logger and a null Component. More...
 
template<typename Component_payload >
 Log_context (Logger *logger, Component_payload component_payload)
 Constructs Log_context by storing the given pointer to a Logger and a new Component storing the specified generically typed payload (an enum value). More...
 
 Log_context (const Log_context &src)
 Copy constructor that stores equal Logger* and Component values as the source. More...
 
 Log_context (Log_context &&src)
 Move constructor that makes this equal to src, while the latter becomes as-if default-constructed. More...
 
Log_contextoperator= (const Log_context &src)
 Assignment operator that behaves similarly to the copy constructor. More...
 
Log_contextoperator= (Log_context &&src)
 Move assignment operator that behaves similarly to the move constructor. More...
 
void swap (Log_context &other)
 Swaps Logger pointers and Component objects held by *this and other. More...
 
Loggerget_logger () const
 Returns the stored Logger pointer, particularly as many FLOW_LOG_*() macros expect. More...
 
const Componentget_log_component () const
 Returns reference to the stored Component object, particularly as many FLOW_LOG_*() macros expect. More...
 

Protected Member Functions

 Peer_socket (log::Logger *logger_ptr, util::Task_engine *task_engine, const Peer_socket_options &opts)
 Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily meaningful values. More...
 

Related Functions

(Note that these are not member functions.)

std::ostream & operator<< (std::ostream &os, const Peer_socket *sock)
 Prints string representation of given socket to given standard ostream and returns the latter. More...
 

Additional Inherited Members

- Static Public Member Functions inherited from flow::util::Shared_ptr_alias_holder< boost::shared_ptr< Peer_socket > >
static Ptr ptr_cast (const From_ptr &ptr_to_cast)
 Provides syntactic-sugary way to perform a static_pointer_cast<> from a compatible smart pointer type From_ptr, typically From_ptr::element_type being in the same class hierarchy as Target_ptr::element_type. More...
 
static Const_ptr const_ptr_cast (const From_ptr &ptr_to_cast)
 Identical to ptr_cast() but adds const-ness (immutability) to the pointed-to type. More...
 
static Ptr dynamic_ptr_cast (const From_ptr &ptr_to_cast)
 Equivalent to ptr_cast() but a dynamic_pointer_cast instead of static. More...
 
static Const_ptr dynamic_const_ptr_cast (const From_ptr &ptr_to_cast)
 Identical to const_ptr_cast() but a dynamic_pointer_cast instead of static. More...
 

Detailed Description

A peer (non-server) socket operating over the Flow network protocol, with optional stream-of-bytes and reliability support.

Reliability is enabled or disabled via a socket option, Peer_socket_options::m_st_rexmit_on, at socket creation. Use unreliable mode with care – see send() method doc header for details.

Life cycle of a Peer_socket

A given Peer_socket can arise either by connecting to Server_socket on a Node (Node::connect() or Node::sync_connect()), or by listening on a Node's Server_socket and accepting such a connection (Server_socket::accept() or Server_socket::sync_accept()). In all cases, Node or Server_socket generates a new Peer_socket and returns it (factory pattern). Peer_socket is not instantiable otherwise. A Peer_socket cannot be deleted explicitly by the user and will only be returned via boost::shared_ptr<>; when both the Node and all user code no longer refers to it, the Peer_socket will be destroyed.

Once a net_flow user has a Peer_socket object, that object represents a socket in one of the following basic states:

Open.Connecting means means Node initiated a connect to the given server, and this is in progress. Open.Connected means the connection to the other Node is fully functional. Open.Disconnecting means either our side or the other side has initiated a clean or abrupt disconnect, but it is not yet entirely finished (background handshaking is happening, you have not read all available data or sent all queued data, etc.).

In either case, reading and writing may or may not be possible at a given time, depending on the state of the internal buffers and the data having arrived on the logical connection. Thus all Open sub-states can and often should be treated the same way in a typical Flow-protocol-using algorithm: simply determine when the Peer_socket is Readable, and read; and similarly for Writable and write. Thus the sub-states are distinguished for informational/diagnostic purposes only, as user reading/writing logic in these states should usually be identical.

Todo:
Closing connection considerations. May implement closing only via timeouts at first (as opposed to explicit closing). Below text refers to close_final() and close_start(), but those are just ideas and may be replaced with timeout, or nothing. At this time, the only closing supported is abrupt close due to error or abrupt close via close_abruptly().

Closed means that the Peer_socket has become disconnected, and no data can possibly be received or sent, AND that Node has no more background internal operations to perform and has disowned the Peer_socket. In other words, a Closed Peer_socket is entirely dead.

Exactly the following state transitions are possible for a given Peer_socket returned by Node:

Note, in particular, that Closed is final; socket cannot move from Closed to Open. If after an error or valid disconnection you want to reestablish a connection, obtain a new Peer_socket from Node's factories. Rationale (subject to change): this cuts down on state having to be tracked inside a Peer_socket, while the interface becomes simpler without much impact on usability. Anti-rationale: contradicts BSD socket and boost.asio established practices; potentially more resource-intensive/slower in the event of errors and disconnects. Why IMO rationale > anti-rationale: it's simpler, and the potential problems do not appear immediately serious; added statefulness can be added later if found desirable.

Receving, sending, and buffers: Peer_socket, like a TCP socket, has a Receive buffer (a/k/a FIFO queue of bytes) of some maximum size and a Send buffer (a/k/a FIFO queue of bytes) of some maximum size. They are typically not directly exposed via the interface, but their existence affects documented behavior. I formally describe them here, but generally they work similarly to TCP socket Send/Receive buffers.

The Receive buffer: Contains bytes asynchronously received on the connection that have not yet been removed with a *receive() method. Any bytes that asynchronously arrive on the connection are asynchronously stored to the buffer on the other side of the buffer in a queued fashion.

The Send buffer: Contains bytes intended to be asynchronously sent on the connection that have been placed there by a *send() method but not yet sent on the connection. Any bytes that are asynchronously sent on the connection are asynchronously removed from the buffer on the other side of the buffer in a queued fashion.

With that in mind, here are the definitions of Readable and Writable while state is Open:

Note that neither definition really cares about the state of the network connection (e.g., could bytes actually be sent over the network at the moment?). There is one caveat: A socket is not Writable until Open.Connecting state is transitioned away from; this prevents user from buffering up send data before the connection is ready. (Allowing that would not necessarily be wrong, but I'm taking a cue from BSD socket semantics on this, as they seem to be convenient.)

In Open, the following archetypal operations are provided. (In Closed all immediately fail; in Open.Disconnecting some immediately fail if close*() has been called.) Let R be the current size of data in the Receive buffer, and S be the available space for data in the Send buffer.

These are similar to TCP Receive and Send APIs in non-blocking mode, and TCP Receive and Send APIs in blocking mode, respectively. There may be other similarly themed methods, but all use these as semantic building blocks.

To understand the order of events, one can think of a disconnect-causing event (like a graceful close initiation from the remote socket) as a piece of data itself. Thus, for example, if 5 bytes are received and placed into the Receive buffer without being read by the user, and then a connection close is detected, the socket will be Readable until the 5 bytes have been receive()ed, and the next receive() (or send()) would yield the error, since that's the order things happened. Similarly, suppose you've sent 5 bytes, but they haven't been yet sent over the wire and are sitting in the Send buffer. Then you trigger a graceful connection close. First the 5 bytes will be sent if possible, and then the closing procedure will actually begin.

Abrupt closes such as connection resets may force both buffers to be immediately emptied without giving to the user or writing to the other side, so that the above rule does not have to apply. Typically a connection reset means the socket is immediately unusable no matter what was in the buffers at the time, per BSD socket semantics.

Efficiently reading/writing

The sync_*() methods are efficient, in that they use no processor cycles until Readable or Writable is achieved (i.e., they sleep until that point). The non-blocking versions don't sleep/block, however. For a program using them to be efficient it should sleep until Readable or Writable and only then call receive()/send(), when data are certainly available for immediate reading or writing. Moreover, a complex program is likely to want to perform this sleep-and-conditional-wake on a set of several Peer_socket objects simultaneously (similarly to select(), epoll*(), etc.). Use class Event_set for this purpose.

Thread safety

Same as for Node. (Briefly: all operations safe for simultaneous execution on separate or the same object.)

Member Enumeration Documentation

◆ Open_sub_state

The sub-state of a Peer_socket when state is State::S_OPEN.

Enumerator
S_CONNECTING 

This Peer_socket was created through an active connect (Node::connect() and the like), and the connection to the remote Node is currently being negotiated by this socket's Node.

A socket in this state may be Writable but cannot be Readable. However, except for diagnostic purposes, this state should generally be treated the same as S_CONNECTED.

S_CONNECTED 

This Peer_socket was created through a passive connect (Node::accept() and the like) or an active connect (Node::connect() and the like), and the connection is (as far this socket's Node knows) set up and functioning.

A socket in this state may be Writable or Readable.

S_DISCONNECTING 

This Peer_socket was created through a passive connect (Node::accept() and the like) or an active connect (Node::connect() and the like), but since then either an active close, passive close, or an error has begun to close the connection, but data may still possibly arrive and be Readable; also data may have been "sent" but still sitting in the Send buffer and needs to be sent over the network.

A socket in this state may be Readable but cannot be Writable.

This implies that a non-S_CLOSED socket may be, at a lower level, disconnected. For example, say there are 5 bytes in the Receive buffer, and the other side sends a graceful disconnect packet to this socket. This means the connection is finished, but the user can still receive() the 5 bytes (without blocking). Then state will remain S_OPEN.S_DISCONNECTING until the last of the 5 bytes is received (gone from the buffer); at this point state may change to S_CLOSED (pending any other work Node must do to be able to disown the socket).

◆ State

State of a Peer_socket.

Enumerator
S_OPEN 

Future reads or writes may be possible. A socket in this state may be Writable or Readable.

S_CLOSED 

Neither future reads nor writes are possible, AND Node has disowned the Peer_socket.

Constructor & Destructor Documentation

◆ Peer_socket()

flow::net_flow::Peer_socket::Peer_socket ( log::Logger logger_ptr,
util::Task_engine task_engine,
const Peer_socket_options opts 
)
explicitprotected

Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily meaningful values.

Parameters
logger_ptrThe Logger implementation to use subsequently.
task_engineIO service for the timer(s) stored as data member(s).
optsThe options set to copy into this Peer_socket and use subsequently.

Member Function Documentation

◆ close_abruptly()

void flow::net_flow::Peer_socket::close_abruptly ( Error_code err_code = 0)

Acts as if fatal error error::Code::S_USER_CLOSED_ABRUPTLY has been discovered on the connection.

Does not block.

Post-condition: state() == State::S_CLOSED. Additionally, assuming no loss on the network, the other side will close the connection with error error::Code::S_CONN_RESET_BY_OTHER_SIDE.

Note: Discovering a fatal error on the connection would trigger all event waits on this socket (sync_send(), sync_receive(), Event_set::sync_wait(), Event_set::async_wait()) to execute on-event behavior (return, return, return, invoke handler, respectively). Therefore this method will cause just that, if applicable.

Note: As a corollary, a socket closing this way (or any other way) does NOT cause that socket's events (if any) to be removed from any Event_set objects. Clearing an Event_set of all or some sockets is the Event_set user's responsibility (the classic way being Event_set::close()).

Warning
The moment the other side is informed we have abruptly closed the connection, they will no longer be able to receive() any of it (even if data had been queued up in their Receive buffer).
Todo:
Currently this close_abruptly() is the only way for the user to explicitly close one specified socket. All other ways are due to error (or other side starting graceful shutdown, once we implement that). Once we implement graceful close, via close_start() and close_final(), use of close_abruptly() should be discouraged, or it may even be deprecated (e.g., Nodes lack a way to initiate an abrupt close for a specific socket).
Todo:
close_abruptly() return bool (false on failure)?
Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_NODE_NOT_RUNNING, or – if socket already closed (state() == State::S_CLOSED) – then the error that caused the closure.

◆ disconnect_cause()

Error_code flow::net_flow::Peer_socket::disconnect_cause ( ) const

The error code that perviously caused state() to become State::S_CLOSED, or success code if state is not CLOSED.

For example, error::code::S_CONN_RESET_BY_OTHER_SIDE (if was connected) or error::Code::S_CONN_TIMEOUT (if was connecting)

Returns
Ditto.

◆ get_connect_metadata()

size_t flow::net_flow::Peer_socket::get_connect_metadata ( const boost::asio::mutable_buffer &  buffer,
Error_code err_code = 0 
) const

Obtains the serialized connect metadata, as supplied by the user during the connection handshake.

If this side initiated the connection (Node::connect() and friends), then this will equal what was passed to the connect_with_metadata() (or similar) method. More likely, if this side accepted the connection (Server_socket::accept() and friends), then this will equal what the user on the OTHER side passed to connect_with_metadata() or similar.

Note
It is up to the user to deserialize the metadata portably. One recommended convention is to use boost::endian::native_to_little() (and similar) before connecting; and on the other side use the reverse (boost::endian::little_to_native()) before using the value. Packet dumps will show a flipped (little-endian) representation, while with most platforms the conversion will be a no-op at compile time. Alternatively use native_to_big() and vice-versa.
If a connect() variant without _with_metadata in the name was used, then the metadata are composed of a single byte with the zero value.
Parameters
bufferA buffer to copy the metadata into.
err_codeSee flow::Error_code docs for error reporting semantics.
Returns
The size of the copied metadata.

◆ info()

Peer_socket_info flow::net_flow::Peer_socket::info ( ) const

Returns a structure containing the most up-to-date stats about this connection.

Note
At the cost of reducing locking overhead in 99.999999% of the Peer_socket's operation, this method may take a bit of time to run. It's still probably only 10 times or so slower than a simple lock, work, unlock – there is a condition variable and stuff involved – but this may matter if done very frequently. So you probably should not. (Hmmm... where did I get these estimates, namely "10 times or so"?)
Todo:
Provide a similar info() method that loads an existing structure (for structure reuse).
Returns
See above.

◆ local_port()

flow_port_t flow::net_flow::Peer_socket::local_port ( ) const

The local Flow-protocol port chosen by the Node (if active or passive open) or user (if passive open) for this side of the connection.

For a given Peer_socket, this will always return the same value, even if state is State::S_CLOSED. However, when state is State::S_CLOSED, the port may be unused or taken by another socket.

Returns
See above.

◆ max_block_size()

size_t flow::net_flow::Peer_socket::max_block_size ( ) const

The maximum number of bytes of user data per received or sent packet on this connection.

See Peer_socket_options::m_st_max_block_size. Note that this method is ESSENTIAL when using the socket in unreliable mode (assuming you want to implement reliability outside of net_flow).

Returns
Ditto.

◆ node()

Node * flow::net_flow::Peer_socket::node ( ) const

Node that produced this Peer_socket.

Returns
Pointer to (guaranteed valid) Node; null if state is State::S_CLOSED.

◆ options()

Peer_socket_options flow::net_flow::Peer_socket::options ( ) const

Copies this socket's option set and returns that copy.

If you intend to use set_options() to modify a socket's options, we recommend you make the modifications on the copy returned by options().

Todo:
Provide a similar options() method that loads an existing structure (for structure reuse).
Returns
See above.

◆ receive()

template<typename Mutable_buffer_sequence >
size_t flow::net_flow::Peer_socket::receive ( const Mutable_buffer_sequence &  target,
Error_code err_code = 0 
)

Receives (consumes from the Receive buffer) bytes of data, up to a given maximum cumulative number of bytes as inferred from size of provided target buffer sequence.

The data are copied into the user's structure and then removed from the Receive buffer.

The method does not block. In particular if there are no data already received from the other side, we return no data.

If the provided buffer has size zero, the method is a NOOP other than possibly logging.

Error handling

These are the possible outcomes.

  1. There are no data in the Receive buffer. Socket not Readable. 0 is returned; *err_code is set to success unless null; no data returned.
  2. The socket is not yet fully connected (S_OPEN+S_CONNECTING). Socket not Readable. 0 is returned; *err_code is set to success unless null; no data returned.
  3. There are data in the Receive buffer; and socket is fully connected (S_OPEN+S_CONNECTED) or gracefully shutting down (S_OPEN+S_DISCONNECTING). Socket Readable. >= 1 is returned; *err_code is set to success; data returned.
  4. The operation cannot proceed due to an error. 0 is returned; *err_code is set to the specific error; no data buffered. (If err_code null, Runtime_error thrown.)

The semantics of -3- (the success case) are as follows. N bytes will be copied from Receive buffer beginning at the start of the Mutable_buffer_sequence target. These N bytes may be spread across 1 or more buffers in that sequence; the subdivision structure of the sequence of bytes into buffers has no effect on the bytes, or order thereof, that will be moved from the Receive buffer (e.g., target could be N+ 1-byte buffers, or one N+-byte buffer – the popped Receive buffer would be the same, as would be the extracted bytes). N equals the smaller of: the available bytes in the Receive buffer; and buffer_size(target). We return N.

Reliability and ordering guarantees

See the send() doc header.

Template Parameters
Mutable_buffer_sequenceType that models the boost.asio MutableBufferSequence concept (see Boost docs). Basically, it's any container with elements convertible to boost::asio::mutable_buffer; and bidirectional iterator support. Examples: vector<mutable_buffer>, list<mutable_buffer>. Why allow mutable_buffer instead of, say, Sequence of bytes? Same reason as boost.asio's receive functions: it allows a great amount of flexibility without sacrificing performance, since boost::asio::buffer() function can adapt lots of different objects (arrays, vectors, strings, and more of bytes, integers, and more).
Parameters
targetBuffer sequence to which a stream of bytes to consume from Receive buffer will be written.
err_codeSee flow::Error_code docs for error reporting semantics. Error implies that neither this receive() nor any subsequent receive() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
The number of bytes consumed (placed into target). Always 0 if bool(*err_code) == true when receive() returns.

◆ remote_endpoint()

const Remote_endpoint & flow::net_flow::Peer_socket::remote_endpoint ( ) const

Intended other side of the connection (regardless of success, failure, or current State).

For a given Peer_socket, this will always return the same value, even if state is State::S_CLOSED.

Returns
See above.

◆ send()

template<typename Const_buffer_sequence >
size_t flow::net_flow::Peer_socket::send ( const Const_buffer_sequence &  data,
Error_code err_code = 0 
)

Sends (adds to the Send buffer) the given bytes of data up to a maximum internal buffer size; and asynchronously sends them to the other side.

The data given is copied into *this, in the order given. Only as many bytes as possible without the Send buffer size exceeding a certain max are copied.

The method does not block. Data are then sent asynchronously (in the background).

Method does nothing except possibly logging if there are no bytes in data.

Error handling

These are the possible outcomes.

  1. There is no space in the Send buffer (usually due to network congestion). Socket not Writable. 0 is returned; *err_code is set to success unless null; no data buffered.
  2. The socket is not yet fully connected (S_OPEN+S_CONNECTING state). Socket not Writable. 0 is returned; *err_code is set to success unless null; no data buffered.
  3. There is space in the Send buffer, and socket connection is open (S_OPEN+S_CONNECTED). Socket Writable. >= 1 is returned; *err_code is set to success; data buffered.
  4. The operation cannot proceed due to an error. 0 is returned; *err_code is set to the specific error unless null; no data buffered. (If err_code null, Runtime_error thrown.)

The semantics of -3- (the success case) are as follows. N bytes will be copied into Send buffer from the start of the Const_buffer_sequence data. These N bytes may be spread across 1 or more buffers in that sequence; the subdivision structure of the sequence of bytes into buffers has no effect on what will be buffered in Send buffer (e.g., "data" could be N+ 1-byte buffers, or one N+-byte buffer – the result would be the same). N equals the smaller of: the available space in the Send buffer; and buffer_size(data). We return N.

Reliability and ordering guarantees: if the socket option rexmit-on is enabled

Reliability and ordering are guaranteed, and there is no notion of message boundaries. There is no possibility of data duplication. In other words full stream-of-bytes functionality is provided, as in TCP.

Reliability and ordering guarantees: if the socket option rexmit-on is NOT enabled

NO reliability guarantees are given, UNLESS ALL calls to send() (and other *send() methods) satisfy the condition: 'buffer_size(data) is a multiple of sock->max_block_size()'; AND all calls to receive() (and other *receive() methods) on the OTHER side satisfy the condition: 'buffer_size(target) is a multiple of sock->max_block_size().' If and only if these guidelines are followed, and there is no connection closure, the following reliability guarantee is made:

Let a "block" be a contiguous chunk of bytes in a "data" buffer sequence immediately following another "block," except the first "block" in a connection, which begins with the first byte of the "data" buffer sequence passed to the first *send() call on that connection. Then: Each given block will either be available to *receive() on the other side exactly once and without corruption; or not available to *receive() at all. Blocks may arrive in a different order than specified here, including with respect to other *send() calls performed before or after this one. In other words, these are guaranteed: block boundary preservation, protection against corruption, protection again duplication. These are not guaranteed: order preservation, delivery. Informally, the latter factors are more likely to be present on higher quality network paths.

Template Parameters
Const_buffer_sequenceType that models the boost.asio ConstBufferSequence concept (see Boost docs). Basically, it's any container with elements convertible to boost::asio::const_buffer; and bidirectional iterator support. Examples: vector<const_buffer>, list<const_buffer>. Why allow const_buffer instead of, say, Sequence of bytes? Same reason as boost.asio's send functions: it allows a great amount of flexibility without sacrificing performance, since boost::asio::buffer() function can adapt lots of different objects (arrays, vectors, strings, and more – composed of bytes, integers, and more).
Parameters
dataBuffer sequence from which a stream of bytes to add to Send buffer will be obtained.
err_codeSee flow::Error_code docs for error reporting semantics. Error implies that neither this send() nor any subsequent *send() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
Number of bytes (possibly zero) added to buffer. Always 0 if bool(*err_code) == true when send() returns.

◆ set_options()

bool flow::net_flow::Peer_socket::set_options ( const Peer_socket_options opts,
Error_code err_code = 0 
)

Dynamically replaces the current options set (options()) with the given options set.

Only those members of opts designated as dynamic (as opposed to static) may be different between options() and opts. If this is violated, it is an error, and no options are changed.

Typically one would acquire a copy of the existing options set via options(), modify the desired dynamic data members of that copy, and then apply that copy back by calling set_options().

Parameters
optsThe new options to apply to this socket. It is copied; no reference is saved.
err_codeSee flow::Error_code docs for error reporting semantics. Generated codes: error::Code::S_STATIC_OPTION_CHANGED, error::Code::S_OPTION_CHECK_FAILED, error::Code::S_NODE_NOT_RUNNING.
Returns
true on success, false on error.

◆ state()

Peer_socket::State flow::net_flow::Peer_socket::state ( Open_sub_state open_sub_state = 0) const

Current State of the socket.

Parameters
open_sub_stateIgnored if null. Otherwise, if and only if State::S_OPEN is returned, *open_sub_state is set to the current sub-state of S_OPEN.
Returns
Current main state of the socket.

◆ sync_receive() [1/4]

template<typename Rep , typename Period >
bool flow::net_flow::Peer_socket::sync_receive ( const boost::asio::null_buffers &  ,
const boost::chrono::duration< Rep, Period > &  max_wait,
Error_code err_code = 0 
)

sync_receive() operating in null_buffers mode, wherein – if Readable state is reached – the actual data are not moved into any buffer, leaving that to the caller to do if desired.

Hence, this is a way of waiting for Readable state that could be more concise in some situations than Event_set::sync_wait().

Error handling

These are the possible outcomes:

  1. There are data in the Receive buffer; and socket is fully connected (S_OPEN+S_CONNECTED) or gracefully shutting down (S_OPEN+S_DISCONNECTING). Socket Readable. true is returned; *err_code is set to success unless null.
  2. The operation cannot proceed due to an error. false is returned; *err_code is set to the specific error unless null. *err_code == S_WAIT_INTERRUPTED means the wait was interrupted (similarly to POSIX's EINTR). (If err_code null, Runtime_error thrown.)
  3. Neither condition above is detected before the timeout expires (if provided). Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT.

Note that it is NOT possible to return false and no error.

Tip: Typical types you might use for max_wait: boost::chrono::milliseconds, boost::chrono::seconds, boost::chrono::high_resolution_clock::duration.

Template Parameters
RepSee other sync_receive().
PeriodSee other sync_receive().
Parameters
max_waitSee other sync_receive().
err_codeSee flow::Error_code docs for error reporting semantics. Error, except WAIT_INTERRUPTED or WAIT_USER_TIMEOUT, implies that neither this nor any subsequent receive() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
true if there are 1+ bytes ready to read; false if either a timeout has occurred (no bytes ready), or another error has occurred.

◆ sync_receive() [2/4]

bool flow::net_flow::Peer_socket::sync_receive ( const boost::asio::null_buffers &  tag,
Error_code err_code = 0 
)

Equivalent to sync_receive(null_buffers(), duration::max(), err_code); i.e., sync_receive(null_buffers) with no timeout.

Parameters
err_codeSee other sync_receive().
tagTag argument.
Returns
See other sync_receive().

◆ sync_receive() [3/4]

template<typename Rep , typename Period , typename Mutable_buffer_sequence >
size_t flow::net_flow::Peer_socket::sync_receive ( const Mutable_buffer_sequence &  target,
const boost::chrono::duration< Rep, Period > &  max_wait,
Error_code err_code = 0 
)

Blocking (synchronous) version of receive().

Acts just like receive(), except that if socket is not immediately Readable (i.e., receive() would return 0 and no error), waits until it is Readable (receive() would return either >0, or 0 and an error) and returns receive(target, err_code). If a timeout is specified, and this timeout expires before socket is Readable, it acts as if receive() produced error::Code::S_WAIT_USER_TIMEOUT.

Error handling

These are the possible outcomes:

  1. There are data in the Receive buffer; and socket is fully connected (S_OPEN+S_CONNECTED) or gracefully shutting down (S_OPEN+S_DISCONNECTING). Socket Readable. >= 1 is returned; *err_code is set to success unless null; data returned.
  2. The operation cannot proceed due to an error. 0 is returned; *err_code is set to the specific error unless null; no data buffered. *err_code == S_WAIT_INTERRUPTED means the wait was interrupted (similarly to POSIX's EINTR). (If err_code null, Runtime_error thrown.)
  3. Neither condition above is detected before the timeout expires (if provided). Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT.

The semantics of -1- (the success case) equal those of receive().

Note that it is NOT possible to return 0 and no error.

Tip: Typical types you might use for max_wait: boost::chrono::milliseconds, boost::chrono::seconds, boost::chrono::high_resolution_clock::duration.

See also
The version of sync_receive() with no timeout.
Template Parameters
RepSee boost::chrono::duration documentation (and see above tip).
PeriodSee boost::chrono::duration documentation (and see above tip).
Mutable_buffer_sequenceSee receive().
Parameters
targetSee receive().
max_waitThe maximum amount of time from now to wait before giving up on the wait and returning. "duration<Rep, Period>::max()" will eliminate the time limit and cause indefinite wait (i.e., no timeout).
err_codeSee flow::Error_code docs for error reporting semantics. Error, except WAIT_INTERRUPTED or WAIT_USER_TIMEOUT, implies that neither this receive() nor any subsequent receive() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
Number of bytes (possibly zero) added to target. Always 0 if bool(*err_code) == true when sync_receive() returns.

◆ sync_receive() [4/4]

template<typename Mutable_buffer_sequence >
size_t flow::net_flow::Peer_socket::sync_receive ( const Mutable_buffer_sequence &  target,
Error_code err_code = 0 
)

Equivalent to sync_receive(target, duration::max(), err_code); i.e., sync_receive() with no timeout.

Template Parameters
Mutable_buffer_sequenceSee other sync_receive().
Parameters
targetSee other sync_receive().
err_codeSee other sync_receive().
Returns
See other sync_receive().

◆ sync_send() [1/4]

template<typename Rep , typename Period >
bool flow::net_flow::Peer_socket::sync_send ( const boost::asio::null_buffers &  ,
const boost::chrono::duration< Rep, Period > &  max_wait,
Error_code err_code = 0 
)

sync_send() operating in null_buffers mode, wherein – if Writable state is reached – the actual data are not moved out of any buffer, leaving that to the caller to do if desired.

Hence, this is a way of waiting for Writable state that could be more concise in some situations than Event_set::sync_wait().

Error handling

These are the possible outcomes:

  1. There is space in the Send buffer; and socket is fully connected (S_OPEN+S_CONNECTED). Socket Writable. true is returned; *err_code is set to success unless null.
  2. The operation cannot proceed due to an error. false is returned; *err_code is set to the specific error unless null. *err_code == S_WAIT_INTERRUPTED means the wait was interrupted (similarly to POSIX's EINTR). (If err_code null, Runtime_error thrown.)
  3. Neither condition above is detected before the timeout expires (if provided). Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT.

Note that it is NOT possible to return false and no error.

Tip: Typical types you might use for max_wait: boost::chrono::milliseconds, boost::chrono::seconds, boost::chrono::high_resolution_clock::duration.

Template Parameters
RepSee other sync_send().
PeriodSee other sync_send().
Parameters
max_waitSee other sync_receive().
err_codeSee flow::Error_code docs for error reporting semantics. Error, except WAIT_INTERRUPTED or WAIT_USER_TIMEOUT, implies that neither this nor any subsequent send() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
true if 1+ bytes are possible to add to Send buffer; false if either a timeout has occurred (bytes not writable), or another error has occurred.

◆ sync_send() [2/4]

bool flow::net_flow::Peer_socket::sync_send ( const boost::asio::null_buffers &  tag,
Error_code err_code = 0 
)

Equivalent to sync_send(null_buffers(), duration::max(), err_code); i.e., sync_send(null_buffers) with no timeout.

Parameters
err_codeSee other sync_receive().
tagTag argument.
Returns
See other sync_receive().

◆ sync_send() [3/4]

template<typename Rep , typename Period , typename Const_buffer_sequence >
size_t flow::net_flow::Peer_socket::sync_send ( const Const_buffer_sequence &  data,
const boost::chrono::duration< Rep, Period > &  max_wait,
Error_code err_code = 0 
)

Blocking (synchronous) version of send().

Acts just like send(), except that if Socket is not immediately Writable (i.e., send() would return 0 and no error), waits until it is Writable (send() would return either >0, or 0 and an error) and returns send(data, err_code). If a timeout is specified, and this timeout expires before socket is Writable, acts like send() executed on an un-Writable socket.

Error handling

These are the possible outcomes (assuming there are data in the argument data).

  1. There is space in the Send buffer, and socket connection is open (S_OPEN+S_CONNECTED). Socket Writable. >= 1 is returned; *err_code is set to success unless null; data buffered.
  2. The operation cannot proceed due to an error. 0 is returned; *err_code is set to the specific error unless null; no data buffered. (If err_code null, Runtime_error thrown.) The code error::Code::S_WAIT_INTERRUPTED means the wait was interrupted (similarly to POSIX's EINTR).
  3. Neither condition above is detected before the timeout expires (if provided). Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT.

The semantics of -1- (the success case) equal those of send().

Note that it is NOT possible to return 0 and no error.

Tip: Typical types you might use for max_wait: boost::chrono::milliseconds, boost::chrono::seconds, boost::chrono::high_resolution_clock::duration.

See also
The version of sync_send() with no timeout.
Template Parameters
RepSee boost::chrono::duration documentation (and see above tip).
PeriodSee boost::chrono::duration documentation (and see above tip).
Const_buffer_sequenceSee send().
Parameters
dataSee send().
max_waitThe maximum amount of time from now to wait before giving up on the wait and returning. "duration<Rep, Period>::max()" will eliminate the time limit and cause indefinite wait (i.e., no timeout).
err_codeSee flow::Error_code docs for error reporting semantics. Error, except WAIT_INTERRUPTED or WAIT_USER_TIMEOUT, implies that neither this send() nor any subsequent send() on this socket will succeeed. (In particular a clean disconnect is an error.)
Returns
Number of bytes (possibly zero) added to Send buffer. Always 0 if bool(*err_code) == true when sync_send() returns.

◆ sync_send() [4/4]

template<typename Const_buffer_sequence >
size_t flow::net_flow::Peer_socket::sync_send ( const Const_buffer_sequence &  data,
Error_code err_code = 0 
)

Equivalent to sync_send(data, duration::max(), err_code); i.e., sync_send() with no timeout.

Template Parameters
Const_buffer_sequenceSee other sync_send().
Parameters
dataSee other sync_send().
err_codeSee other sync_send().
Returns
See other sync_send().

Friends And Related Function Documentation

◆ operator<<()

std::ostream & operator<< ( std::ostream &  os,
const Peer_socket sock 
)
related

Prints string representation of given socket to given standard ostream and returns the latter.

The representation includes the local and remote endpoints and the hex pointer value.

Note
shared_ptr forwards ostream output to the underlying pointer type, so this will affect Ptr output as well.
Parameters
osStream to print to.
sockObject to serialize. May be null.
Returns
os.

The documentation for this class was generated from the following files: