Flow 1.0.2
Flow project: Full implementation reference.
Public Types | Public Member Functions | Protected Member Functions | Private Types | Private Member Functions | Private Attributes | Friends | Related Functions | List of all members
flow::net_flow::Server_socket Class Reference

A server socket able to listen on a single Flow port for incoming connections and return peer sockets (Peer_socket objects) to the local user once such connections are established. More...

#include <server_socket.hpp>

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

Public Types

enum class  State { S_LISTENING , S_CLOSING , S_CLOSED }
 State of a Server_socket. More...
 
using Peer_socket_ptr = boost::shared_ptr< Peer_socket >
 Equivalent to Peer_socket::Ptr, but can't use that due to C++'s circular reference nonsense. More...
 
- Public Types inherited from flow::util::Shared_ptr_alias_holder< boost::shared_ptr< Server_socket > >
using Ptr = boost::shared_ptr< Server_socket >
 Short-hand for ref-counted pointer to mutable values of type Target_type::element_type (a-la T*). More...
 
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 *). More...
 

Public Member Functions

 ~Server_socket () override
 Boring virtual destructor. Note that deletion is to be handled exclusively via shared_ptr, never explicitly. More...
 
State state () const
 Current State of the socket. More...
 
Nodenode () const
 Node that produced this Server_socket. More...
 
flow_port_t local_port () const
 The local Flow-protocol port on which this server is or was listening. More...
 
Peer_socket_ptr accept (Error_code *err_code=0)
 Non-blocking accept: obtain socket for the least recently established not-yet-obtained peer connection on this server. More...
 
template<typename Rep , typename Period >
Peer_socket_ptr sync_accept (const boost::chrono::duration< Rep, Period > &max_wait, bool reactor_pattern=false, Error_code *err_code=0)
 Blocking (synchronous) version of accept(). More...
 
Peer_socket_ptr sync_accept (bool reactor_pattern=false, Error_code *err_code=0)
 Equivalent to sync_accept(duration::max(), reactor_pattern, err_code); i.e., sync_accept() with no user timeout. 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

 Server_socket (log::Logger *logger, const Peer_socket_options *child_sock_opts)
 Constructs object; initializes most values to well-defined (0, empty, etc.) but not necessarily meaningful values. More...
 

Private Types

using Mutex = util::Mutex_recursive
 Short-hand for reentrant mutex type. More...
 
using Lock_guard = util::Lock_guard< Mutex >
 Short-hand for RAII lock guard of Mutex. More...
 

Private Member Functions

Peer_socket_ptr sync_accept_impl (const Fine_time_pt &wait_until, bool reactor_pattern, Error_code *err_code)
 Same as sync_accept() but uses a Fine_clock-based Fine_duration non-template type for implementation convenience and to avoid code bloat to specify timeout. More...
 

Private Attributes

Peer_socket_options const *const m_child_sock_opts
 Either null or the pointer to a copy of the template Peer_socket_options intended for resulting Peer_socket objects. More...
 
State m_state
 See state(). Should be set before user gets access to *this. Must not be modified by non-W threads. More...
 
Nodem_node
 See node(). More...
 
flow_port_t m_local_port
 See local_port(). More...
 
util::Linked_hash_set< Peer_socket_ptrm_unaccepted_socks
 Queue of passively opened sockets in Peer_socket::Int_state::S_ESTABLISHED internal state that have not yet been claimed by the user via *accept(). More...
 
Error_code m_disconnect_cause
 The Error_code causing this server's move from LISTENING state (if this has occurred); otherwise a clear (success) Error_code. More...
 
Mutex m_mutex
 This object's mutex. More...
 
boost::unordered_set< Peer_socket_ptrm_connecting_socks
 Set of passively opening sockets in pre-ESTABLISHED (so SYN_RCVD?) internal state (and thus are not yet ready to be given out via *accept()). More...
 

Friends

class Node
 See rationale for friending Node in class Server_socket documentation header. More...
 

Related Functions

(Note that these are not member functions.)

std::ostream & operator<< (std::ostream &os, const Server_socket *serv)
 Prints string representation of given socket to given standard ostream and returns the latter. More...
 
std::ostream & operator<< (std::ostream &os, Server_socket::State state)
 Prints string representation of given socket state 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< Server_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 server socket able to listen on a single Flow port for incoming connections and return peer sockets (Peer_socket objects) to the local user once such connections are established.

Life cycle of a Server_socket

A given Server_socket can only arise by calling Node::listen(). Node generates a new Server_socket and returns it (factory pattern). Server_socket is not instantiable otherwise. A Server_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 Server_socket will be destroyed.

Once a Flow-protocol user has a Server_socket object, that object represents a socket in one of the following basic states:

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

Note, in particular, that Closed is final; socket cannot move from Closed to Listening. If after an error or valid closing you want to reestablish a server, obtain a new Server_socket from Node's factory. Same rationale as for equivalent design decision in Peer_socket.

Closing means either this side or other side initiated the closing of this server socket for any reason, but Node is still finishing up operations related to that in the background (such as closing in-progress peer connections). Closed means Node has finished any such operations and has disowned the Server_socket.

In either case the only operation Server_socket supports (accept() and derivatives thereof) is impossible in Closing and Closed. Therefore the two states are distinguished for diagnostic/informational purposes only. Generally one should only accept() when Acceptable, and accept() will return an error if the state is Closing or Closed.

Note that a Closing or Closed Server_socket does NOT mean that already accept()ed Peer_socket objects will be in any way affected.

Todo:
Implement Server_socket::close() functionality – at least the equivalent of Peer_socket::close_abruptly().

Accept queue

Server_socket, like a TCP server socket, stores a queue of fully established peer connections. A Peer_socket is placed onto this internal queue only once its state() == State::S_OPEN (i.e., the connection handshake is finished/successful). Peer_socket objects are then obtained from this queue via the Server_socket::accept() call and its derivatives.

Efficiently accept()ing

The sync_accept() method is efficient, in that it uses no processor cycles until Acceptable is achieved (i.e., it sleeps until that point). The non-blocking accept() method doesn't sleep/block, however. For a program using it to be efficient it should sleep until Acceptable and only then call accept(), when a Peer_socket is certainly available for immediate acceptance. Moreover, a complex program is likely to want to perform this sleep-and-conditional-wake on a set of several Server_sockets (and/or other sockets) 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.)

Implementation notes

See Peer_socket documentation header. Similar comments apply here.

To prevent node.cpp from being unmanageably large (and also because it makes sense), implementations for Node methods that deal only with an individual Server_socket objects reside in server_socket.cpp (even though they are members of Node, since, again, the logic is all forwarded to Node).

Todo:
Limit Server_socket listen queue/set length.
Todo:
Rename State to Phase, as with similar to-do in class Peer_socket doc header.

Definition at line 118 of file server_socket.hpp.

Member Typedef Documentation

◆ Lock_guard

Short-hand for RAII lock guard of Mutex.

Definition at line 308 of file server_socket.hpp.

◆ Mutex

Short-hand for reentrant mutex type.

We explicitly rely on reentrant behavior, so this isn't "just in case."

Todo:
This doc header for Server_socket::Mutex should specify what specific behavior requires mutex reentrance, so that for example one could reevaluate whether there's a sleeker code pattern that would avoid it.

Definition at line 305 of file server_socket.hpp.

◆ Peer_socket_ptr

Equivalent to Peer_socket::Ptr, but can't use that due to C++'s circular reference nonsense.

Definition at line 142 of file server_socket.hpp.

Member Enumeration Documentation

◆ State

State of a Server_socket.

Enumerator
S_LISTENING 

Future or current accept()s may be possible. A socket in this state may be Acceptable.

S_CLOSING 

No accept()s are or will be possible, but Node is still finishing up the closing operation.

S_CLOSED 

No accept()s are or will be possible, AND Node has disowned the Server_socket.

Definition at line 131 of file server_socket.hpp.

Constructor & Destructor Documentation

◆ ~Server_socket()

flow::net_flow::Server_socket::~Server_socket ( )
override

Boring virtual destructor. Note that deletion is to be handled exclusively via shared_ptr, never explicitly.

Definition at line 45 of file server_socket.cpp.

References FLOW_LOG_TRACE, and m_child_sock_opts.

◆ Server_socket()

flow::net_flow::Server_socket::Server_socket ( log::Logger logger,
const Peer_socket_options child_sock_opts 
)
explicitprotected

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

Parameters
loggerThe Logger implementation to use subsequently.
child_sock_optsPointer to a per-socket options struct to copy and store; for each Peer_socket resulting from this Server_socket, the options will be a copy of this copy. If null pointer, then instead the enclosing Node's global per-socket options will be used to produce the copy.

Definition at line 30 of file server_socket.cpp.

References FLOW_LOG_TRACE.

Referenced by sync_accept_impl().

Here is the caller graph for this function:

Member Function Documentation

◆ accept()

Peer_socket::Ptr flow::net_flow::Server_socket::accept ( Error_code err_code = 0)

Non-blocking accept: obtain socket for the least recently established not-yet-obtained peer connection on this server.

There is a queue (FIFO) of not-yet-claimed connections, and this returns the one at the front of the queue.

If state() is State::S_CLOSING or State::S_CLOSED (i.e., not State::S_LISTENING), this will return null (and an error), even if connections have been queued up. Rationale: BSD sockets act similarly: cannot succeed with accept(s), if s is not listening; also internal implementation is simpler. Anti-rationale: our API is much more amenable to allowing accept()s in that situation; and it would mirror the fact that Peer_socket::receive() can succeed in S_OPEN+S_DISCONNECTING state. Why rationale > anti-rationale: it's a judgment call, and I'm going with simplicity of implementation at least for now.

Todo:
Reconsider allowing successful accept() in State::S_CLOSING state?
Parameters
err_codeSee flow::Error_code docs for error reporting semantics. Note: no available connections is not, in itself, an error. So it's quite possible for null to be returned but *err_code is success.
Returns
A Peer_socket sock with sock->state() == Peer_socket::State::S_OPEN. If no connections are available (including if bool(*err_code) == true), returns null pointer.

Definition at line 75 of file server_socket.cpp.

References accept(), flow::net_flow::Node::accept(), flow::net_flow::Node::ensure_sock_open(), FLOW_ERROR_EXEC_AND_THROW_ON_ERROR, m_mutex, and m_node.

Referenced by accept().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ disconnect_cause()

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

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

Note that once it returns non-success, the returned value subsequently will always be the same.

Returns
Ditto.

Definition at line 64 of file server_socket.cpp.

References m_disconnect_cause, and m_mutex.

Referenced by flow::net_flow::asio::Server_socket::async_accept_impl().

Here is the caller graph for this function:

◆ local_port()

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

The local Flow-protocol port on which this server is or was listening.

For a given Server_socket, this will always return the same value, even if CLOSED. However, when CLOSED, the port may be unused or taken by another socket.

Returns
See above.

Definition at line 70 of file server_socket.cpp.

References m_local_port.

Referenced by operator<<().

Here is the caller graph for this function:

◆ node()

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

Node that produced this Server_socket.

Returns
Pointer to (guaranteed valid) Node; 0 if state is S_CLOSED.

Definition at line 58 of file server_socket.cpp.

References m_mutex, and m_node.

Referenced by flow::net_flow::asio::Server_socket::async_accept_impl().

Here is the caller graph for this function:

◆ state()

Server_socket::State flow::net_flow::Server_socket::state ( ) const

Current State of the socket.

Returns
Current state of the socket.

Definition at line 52 of file server_socket.cpp.

References m_mutex, and m_state.

◆ sync_accept() [1/2]

Peer_socket::Ptr flow::net_flow::Server_socket::sync_accept ( bool  reactor_pattern = false,
Error_code err_code = 0 
)

Equivalent to sync_accept(duration::max(), reactor_pattern, err_code); i.e., sync_accept() with no user timeout.

Parameters
err_codeSee other sync_accept().
reactor_patternSee other sync_accept().
Returns
See other sync_accept().

Definition at line 95 of file server_socket.cpp.

References sync_accept_impl().

Here is the call graph for this function:

◆ sync_accept() [2/2]

template<typename Rep , typename Period >
Server_socket::Peer_socket_ptr flow::net_flow::Server_socket::sync_accept ( const boost::chrono::duration< Rep, Period > &  max_wait,
bool  reactor_pattern = false,
Error_code err_code = 0 
)

Blocking (synchronous) version of accept().

Acts just like accept(), except that if *this is not immediately Acceptable (i.e., accept() would return null and no error), waits until it is Acceptable (accept() would return either non-null, or null and an error) and returns accept(err_code). In reactor_pattern mode (see arg doc), if it were to otherwise return a non-null Peer_socket::Ptr, it instead leaves the ready peer socket available for subsequence acceptance and returns null.

If a timeout is specified, and this timeout expires before socket is Acceptable, acts like accept() executed on an un-Acceptable server socket.

Error handling

These are the possible outcomes.

  1. Connection succeeds before the given timeout expires (or succeeds, if no timeout given). Socket is at least Writable at time of return. If !reactor_pattern then the new socket is returned, and no error is returned via *err_code; otherwise the socket is left available for acceptance, while null is returned, and (similarly to non-reactor-pattern mode) no error is returned via *err_code.
  2. Connection fails before the given timeout expires (or fails, if no timeout given). null is returned, *err_code is set to reason for connection failure unless null. (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. A user timeout is given, and the connection does not succeed before it expires. Output semantics are the same as in 2, with the specific code error::Code::S_WAIT_USER_TIMEOUT. (Rationale: consistent with sync_receive(), sync_send() behavior.)

Note that – if !reactor_pattern – it is NOT possible to return null 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_accept() with no timeout.
Template Parameters
RepSee boost::chrono::duration documentation (and see above tip).
PeriodSee boost::chrono::duration documentation (and see above tip).
Parameters
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).
reactor_patternIf and only if true, and the call successfully waited until server socket became Acceptable, then we return a null pointer and leave the peer socket ready to be accepted by the caller. If false, then in that same situation the socket is accepted and returned. The parameter doesn't affect any other situations.
err_codeSee flow::Error_code docs for error reporting semantics.
Returns
Reference-counted pointer to new Server_socket; or an empty pointer (essentially null). Reminder that in reactor_pattern mode this may be null, yet indeed a socket is Acceptable (the presence or lack of an error indicates whether that's the case).

Definition at line 434 of file server_socket.hpp.

References flow::util::chrono_duration_from_now_to_fine_time_pt(), and sync_accept_impl().

Here is the call graph for this function:

◆ sync_accept_impl()

Peer_socket::Ptr flow::net_flow::Server_socket::sync_accept_impl ( const Fine_time_pt wait_until,
bool  reactor_pattern,
Error_code err_code 
)
private

Same as sync_accept() but uses a Fine_clock-based Fine_duration non-template type for implementation convenience and to avoid code bloat to specify timeout.

Parameters
wait_untilSee sync_accept(timeout). This is the absolute time point corresponding to that. "duration<Rep, Period>::max()" maps to the value Fine_time_pt() (Epoch) for this argument.
reactor_patternSee sync_accept().
err_codeSee sync_accept().
Returns
See sync_accept().

Definition at line 100 of file server_socket.cpp.

References flow::net_flow::Node::accept(), flow::net_flow::Node::ensure_sock_open(), FLOW_ERROR_EXEC_AND_THROW_ON_ERROR, m_mutex, m_node, flow::net_flow::Event_set::S_SERVER_SOCKET_ACCEPTABLE, Server_socket(), sync_accept_impl(), and flow::net_flow::Node::sync_op().

Referenced by sync_accept(), and sync_accept_impl().

Here is the call graph for this function:
Here is the caller graph for this function:

Friends And Related Function Documentation

◆ Node

friend class Node
friend

See rationale for friending Node in class Server_socket documentation header.

See also
Node.

Definition at line 295 of file server_socket.hpp.

◆ operator<<() [1/2]

std::ostream & operator<< ( std::ostream &  os,
const Server_socket serv 
)
related

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

The representation includes the local endpoint 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.
servObject to serialize. May be null.
Returns
os.

Definition at line 881 of file server_socket.cpp.

References local_port().

Here is the call graph for this function:

◆ operator<<() [2/2]

std::ostream & operator<< ( std::ostream &  os,
Server_socket::State  state 
)
related

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

Parameters
osStream to print to.
stateValue to serialize.
Returns
os.

Definition at line 902 of file server_socket.cpp.

Member Data Documentation

◆ m_child_sock_opts

Peer_socket_options const* const flow::net_flow::Server_socket::m_child_sock_opts
private

Either null or the pointer to a copy of the template Peer_socket_options intended for resulting Peer_socket objects.

Null means Peer_socket should use Node::options() as the template instead.

Must be deleted in destructor if not null.

Definition at line 337 of file server_socket.hpp.

Referenced by ~Server_socket().

◆ m_connecting_socks

boost::unordered_set<Peer_socket_ptr> flow::net_flow::Server_socket::m_connecting_socks
private

Set of passively opening sockets in pre-ESTABLISHED (so SYN_RCVD?) internal state (and thus are not yet ready to be given out via *accept()).

This gains meaning only in thread W. This should NOT be accessed outside of thread W and is not protected my a mutex.

Once a socket is acceptable (ESTABLISHED), it is moved from this to m_unaccepted_socks, where it can be claimed by the user. Thus the user never accesses this, and it is maintained by thread W only.

See also
Peer_socket::m_originating_serv for the closely related inverse.

Definition at line 410 of file server_socket.hpp.

◆ m_disconnect_cause

Error_code flow::net_flow::Server_socket::m_disconnect_cause
private

The Error_code causing this server's move from LISTENING state (if this has occurred); otherwise a clear (success) Error_code.

This starts as success and may move to one non-success value and then never change after that. Graceful closing of the server via close() is indeed counted as a non-success value for m_disconnect_cause.

As in TCP net-stacks, one cannot recover from a transmission error or termination on the socket (the "error" EWOULDBLOCK/EAGAIN does not count), which is why this can only go from success -> non-success and never change after that. (boost.asio error would be would_block or try_again when I said EWOUDLBLOCK/EAGAIN informally there.)

How to report this to the user: attempting to *accept() or other operations while m_disconnect_cause is not success => the operation returns this Error_code to the user. Note that even already queued acceptable sockets (m_unaccepted_socks) will no longer be available for acceptance.

This should be success in LISTENING state and failure otherwise.

Thread safety

Since user threads will access this at least via accept(), while thread W may set it when implementing the server socket close, this must be protected by a mutex.

Definition at line 390 of file server_socket.hpp.

Referenced by disconnect_cause().

◆ m_local_port

flow_port_t flow::net_flow::Server_socket::m_local_port
private

See local_port().

Should be set before user gets access to *this and not changed afterwards.

Todo:
Make m_local_port const?

Definition at line 352 of file server_socket.hpp.

Referenced by local_port().

◆ m_mutex

Mutex flow::net_flow::Server_socket::m_mutex
mutableprivate

This object's mutex.

The protected items are m_state, m_node, m_unaccepted_socks, sock->m_originating_serv for each sock in m_unaccepted_socks, and m_disconnect_cause.

Definition at line 397 of file server_socket.hpp.

Referenced by accept(), disconnect_cause(), node(), state(), and sync_accept_impl().

◆ m_node

Node* flow::net_flow::Server_socket::m_node
private

See node().

Should be set before user gets access to *this and not changed, except to 0 when state is S_CLOSED. Must not be modified by non-W threads.

Definition at line 346 of file server_socket.hpp.

Referenced by accept(), node(), and sync_accept_impl().

◆ m_state

State flow::net_flow::Server_socket::m_state
private

See state(). Should be set before user gets access to *this. Must not be modified by non-W threads.

Definition at line 340 of file server_socket.hpp.

Referenced by state().

◆ m_unaccepted_socks

util::Linked_hash_set<Peer_socket_ptr> flow::net_flow::Server_socket::m_unaccepted_socks
private

Queue of passively opened sockets in Peer_socket::Int_state::S_ESTABLISHED internal state that have not yet been claimed by the user via *accept().

back() is the next socket to be accepted (i.e., the one that established connection longest ago). This is not the more restricted queue<>, because sometimes we want to remove things from the middle of it on error. It is not a list<>, because we sometimes (on error) need to erase items from the middle which requires a lookup by stored Peer_socket_ptr value which would be linear-time in a plain list<>.

Write-accessible from thread W and user threads (in accept()) and must be protected by a mutex.

See also
Peer_socket::m_originating_serv for the closely related inverse.

Definition at line 366 of file server_socket.hpp.


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