Cabal operating schema is based upon the concept of "balanced callbacks". The application is hooked into "transmission" and "reception" cabal classes; when data needing to be processed approaches local application sockets, Cabal calls reception objects that the application provides; when the network is detected as free to send new data, Cabal calls the transmission objects provided by the application.
This magic is done directly by the application by calling a Cabal function that is able to process all the pending requests up to the moment; this allow to create application specific networking process scheme, and to finetuen multiprocess and mulithreading application environments. Since the application is free to ask Cabal to process pending (outgoing and incoming) network requests, it can ensure that network data processing is not done concurrently with other heavy internal processing.
The way in which the application handles this incoming data, and the way in which it prepares the data to be sent back to the internet, is outside the scope of Cabal; the application must just provide a method to parse/use/store incoming data efficiently enough to handle all possible incoming messages, and it must provide a way to prepare outgoing data fast enough to be ready when transmissions are called to send it.
Address and sockets are the basic objects that allows network connectivity. Address is an object that abstracts a network address, be it an host name, and IP4 address or (in future) an IPv6 id. Address class holds also the port that is bouind with a certain host. Network and system endianity conversion are done automatically, and support for DNS name resolving is also provided.
Sockets class encapsulates a set of common operations that can be made on single UDP or TCP sockets. It also provide automatized timeout support, so that single socket (or few-sockets) based applications could use timed out based sockets to poll periodically the network and avoid being engaged in forever-blocking requests. Cabal also provides a ServerSocket class (derived from the TCP socket class) that is capable to accept incoming TCP connections.
Used alone, Address and Socket classes (both TCP and UDP) would be enough to lay down a fairly complex network application; still this application would not be capable to handle an high number of sockets (without CPU expensive poll loops that would take out precious resources), yet this basic classes will be interesting also for those developers needing a simple one-to-one connectivity, because they incapsulate C functions into very handy C++ objects and because they support multiplatform isomorphic programming: code once, compile everywhere.
Anyhow, once got accustomed to it, Cabal callback model is so easy to use that you'll want to use it also in very simple applications; as it automatizes the most ungrateful works of a networking application, you may find out that writing receivers and transmitters may be easier and cleaner than managing directly the socket transmissions, and that your final application, for how simple it may be, takes benefit from using the callback model provided by Cabal.
The core of Cabal system is the Channel abstraction. A channel represent a phisical or logical connection towards internet. A correctly written application would associate one channel for each physical interface that must be served. Channels can be given a bandwidth limit (that is an outgoing maximum data rate), that should match network ability; this limit may be dynamically adjousted i.e. to match network connectivity variability or to give boost to transmissions or receptions over time.
Channels interacts with both sockets and connections. Sockets are objects able to receive and send real network packets and data, while connections are "logical" connections between the host and a remote destination. Actually, connections encapsulates necessarily only the "outgoing" part of this abstraction, being the data incoming from remote hosts treated differently. Connection may also optionally reference a reception object, thus being able to represent a full connection with a remote host, when the reception may be determined at network level (usually this may happen in TCP based protocols).
Connections are organized in lines; each line is a set of connections that are all meant to provide data to a remote host. In example, an FTP server based on Cabal may have a connection dedicated to send the client command replies, and another one opened to send downloaded file data. As FTP is TCP based, the connections may also hold the reception that would receive an upload, but this is not strictly necessary at Cabal level; the application may decide to use this feature or to store the reception into another object that is more handy to it.
Connections in a line can be name or indexed; the protocol that the application serves may have a fixed or variable number of connections, and the Line object is an easy place to account this fact; in example, in a FTP server the data connection may or may not be opened at a certain time, so the Line may be used to account this fact, storing the "data" named connection when it is opened and deleting it when it is closed.
Lines are meant to be finally encapsulated by the application in an application-specific object representing a certain client. An application may be willing to account information on a certain remote host, i.e. the amount of downloaded data, a userid/password couple, account rights and so on; in this structure, the application will find handy to have also the LINE that phisically represent the set of CONNECTIONS that are currently open towards a certain remote host. This set (application specific data plus Line) is called Application client representation
Each connection is bound to exactly one transmission, reference exactly one socket (which is the socket that puts it in relationship with the remote host), and may or may not reference only one reception.
Transmissions and receptions are NEVER called concurrently. Instead, they are called on a single thread, the receptions before the transmissions, so that reception object can safely set simple flags, common data area, or safely send messages to the transmission, requesting them to be immediately called; if network is free to send, transmissions will be called as soon as all receptions are done, and they will feed the other side of the communication with the data the reception asked to send; else, they will be called as soon as the netwrok is ready to send.
Sockets are queried by the channel to which they refer for possible incoming data; in case they have some data incoming, the reception object is provided with the socket ready to be read. Then, the reception may send some request, message, data or everything to the internal application processing engine. When the engine thinks it has something to send to the remote clients, it request the channel to schedule the transmission as soon as possible; when the transmission is called, it is provided with a socket that is ready to receive new outgoing data. Now the transmission object is able to send the data that the application prepared for it, or to build on the fly new data to be sent.
Two events are particularily important for the application, and are to be managed by the application itself: a new connection incoming and a remote host disconnection.
Connection incoming may be processed outside the channel main processing loop or inside it; in example if some application runs TCP connections, it will listen a server socket for new connection to be accepted each now and then (usually in the same loop in which the channel processing is periodically called); when a new client is accepted, a new socket is created, and the application may safely add it to the relative channel (as the channel is not currently processing the socket list; the application calls the channel processing loop when afterwards). If the application is running some sort of UDP protocol, a new incoming client will not cause a new socket to be generated; the reception that listen to the UDP socket will just recognize a new client willing to talk with the application (based in its protocol rules), and will have to create a new client application representation instance; this includes a new line with a new connection (and so, a new transmission) on which to feed back the incoming client.
As all the transmissions are processed after all the receptions are done, the receptions can safely add new logical lines and connections both to the application and to the channel.
When a connection goes away, this can be told to Cabal in several ways:
In an UDP server, you will aknowledge that a client went away because it fails to answer some query (or to provide some sort of heartbeat) in a given timeout. The main application network loop will have to scan through all the client representation objects, finding those one that have timed out (or those that have cleanly requested to be disconnected, i.e. by the reception setting a flag on the client object). The application can then just destroy the lines associated with a given client just before calling the main channel loop(s); all the channels on which the line is accounted will discard the connections, and all the connection will call the transmission closing notify functions, before the destroy request is fulfilled.
In TCP based services, the reception object will handle the same socket that is used by connections (and transmissions) to talk with the remote client. When the reception closes a socket, the channel loop detects it and destroys the relative connection (signaling the transmission with the before-to-close callback); the lines are also notified, and the connection are safely removed from them. Notice that the lines are never destroyed; the application will have to find a way to detect if all the connections in a line are closed, and so remove the line. Or, the line may be left alive until the client object representation is alive, and if it connects again later the new connections may just added to the already existing line object.
Receptions may also destroy application specific client representations, which in turn may destroy the lines, if the applicaton thinks this is a sensible behavior: as transmission control is done after all receptions are done, channels will keep up cleanly with the updated situations.
Finally, a transmission may return false if it thinks it has nothing more to do with the remote host. A transmission cannot (should not) delete itself, its connection or the line to which it is bound, but returning false the channel will cleanly delete the connection to which the transmission is attached, calling its termination callback and removing the connection from the line.
Similarily, a reception callback may return false; if this happens, the reception is destroyed and the socket is not used for reception anymore; anyhow, it is not closed and all the transmissions that insists on the socket are left alive; they will have to terminate differently.
If a socket is not needed anymore, and if this is detected by a reception, the reception may both destroy the line that is attached to the socket and return false; this will have the socket dereferenced, and eventually released if there is no reference left to it. This is a common case in simple TCP oriented connections, where the socket being closed by the remote side can be detected in the reception.
The last example exploits the most important Cabal features, and demonstrates how to build UDP based logical connections with Cabal.