Overview of the Socket Library Structure
Handle.png

This diagram tries to give a structural overview of the Socket Library, it does not directly show, how the library is implemented. This will be explained later.

The outside interface to the library is a Handle object. This is the only object, the library user directly interacts with. Every handle references some socket. This is like the ordinary POSIX API: the file descriptor (also called file handle, an integer number) references a socket structure which lives in kernel space. In this library, the Handle object (which is not a simple integer any more but an object) references the Socket (which is part of the implementation). Several handles may reference the same Socket. In contrast to the kernel API, the library employs reference counting to release a socket when the last Handle to it goes out of scope.

The behavior of a Socket is defined by it's Protocol. It is divided into two parts: the policy interface and the protocol interface. Together they provide the complete API for a specific type of Socket as defined by the Protocol. The policy interface provides highly efficient access to the most frequently used operations whereas the protocol interface completes the interface by providing a complete set of all protocol specific operations not found in the policy interface. This structure allows us to combine the benefits of two design methodologies: The policy interface utilizes a policy based design technique and is highly efficient albeit more complex to implement, whereas the protocol interface is based on a more common inheritance architecture which is not as optimized for performance but much simpler to implement. We reduce the complexity of the implementation by reducing the policy interface to a minimal sensible subset of the complete API.

The Policy Interface

The policy of a Socket consists of several parts, called policy axis. Each axis corresponds to one specific interface aspect of the Socket. The exact meaning of the policy axis are defined elsewhere (see The Policy Framework). The Protocol will always provide a complete set of policy classes, one for each axis.

This complete socket policy defines the policy interface of the protocol. This interface is carried over into the Handle. The socket policy as defined in the Handle however may be incomplete. This mans, that the accessible interface of the Socket depends on the type of Handle used. The inherent interface does not change but the view of this interface does if the Handle does not provide the complete policy interface. This feature is very important. It allows to define generic Handle types. A generic Handle with an incompletely defined policy can point to an arbitrary Socket as long as all those policy axis which are defined match those defined in that Socket's protocol. Using such a generic handle decouples the implementation parts using this handle from the other socket aspects (e.g. you may define a generic socket handle for TCP based communication leaving the addressingPolicy undefined which makes your code independent of the type of addressing, IPv4 or IPv6).

This can be described as generalized compile-time polymorphism: A base class reference to some derived class will only give access to a reduced interface (the base class interface) of a class. The class still is of it's derived type (and inherently has the complete interface) but only part of it is accessible via the base class reference. Likewise a generic handle (aka base class reference) will only provide a reduced interface (aka base class interface) to the derived class instance (aka socket).

The Protocol Interface

The protocol interface is provided by a set of protocol facets. Each facet provides a part of the interface. Whereas the policy interface is strictly defined (the number and type of policy axis is fixed and also the possible members provided by the policy interface are fixed), the protocol interface is much more flexible. Any member needed to provide a complete API for the specific protocol may be defined, the number and type of facets combined to provide the complete interface is up to the Protocol implementor. This flexibility is necessary to provide a complete API for every possible protocol.

However this flexibility comes at a cost: To access the protocol interface the user must know the exact protocol of the socket. With other words, the protocol is only accessible if the handle you use is a protocol specific handle. A protocol specific Handle differs from a generic Handle in two ways: It always has a complete policy and it knows the exact protocol type of the socket (which generic handles don't). This allows to access to the complete protocol interface.

Implementation of the Socket Library Structure

In the Implementation, the socket policy is identified by an instance of the senf::SocketPolicy template. The Socket representation is internally represented in a senf::SocketBody which is not outside visible. The Handle is provided by a hierarchy of handle templates. Each Handle template uses template arguments for the policy and/or protocol as needed (see The Handle Hierarchy).

The Handle hierarchy divides the interface into two separate strains: the client interface (senf::ClientSocketHandle and senf::ProtocolClientSocketHandle) provides the interface of a client socket whereas the server interface (senf::ServerSocketHandle and senf::ProtocolServerSocketHandle) provides the interface as used by server sockets.

The protocol interface is implemented using inheritance: The Protocol class inherits from each protocol facet using multiple (virtual public) inheritance. The Protocol class therefore provides the complete protocol API in a unified (see The Protocol Classes).