Infrastructure I/O
Protocol Addition


Design Philosophy:
In a daemon there should be only one place to actually “wait” the process.    This may seem like a religious or political statement, but it has been my experience that this greatly simplifies overall process coding and structure and eliminates one of the most common structural/performance errors, that is, not being able to process any event at any time.    I know, sometime you'd rather not process some kinds of events, but, if you can, your process' throughput (and stability) will be greatly improved.    There is nothing more frustrating than to have a process hang, waiting on some event when an unexpected error occurred and the process isn't looking for it.    If you adopt the single wait mindset in your design, you will always be able to process anything that happens.    In order to accomplish this objective you must have a good set of underlying infrastructure tools.


Low Level Straight Skinny:
The infrastructure's I/O and specifically ioWait, is very flexible and allows the coder to build a daemon that can wait on and service any file or socket descriptor event.    To do this the coder must register callbacks to the socket for processing Input, Output, and Exception events.    This is accomplished using the ioRegWat( int sd, pfi_t rdFunc, pfi_t wtFunc, pfi_t excFunc, void *ctxt ).    The rdFunc and wtFunc must process reads and writes from/to the socket/file, and excFunc must process exceptions.    Right about here I need to say that the coder is responsible to understand the protocol in enough detail so that the support code properly handles reads, writes, and exceptions.    The ctxt (context) should be a structure pointer that contains the sd and any other info required to perform input, output, or exception handling.    The ctxt pointer is the only argument passed to the drivers.    By default a new sd will have the read and exception waits enabled but not the write.    When output interrupts are expected, you must enable them using ioSetWatOut( int sd ), and when the output is complete clear the wait flag with ioClrWatOut( int sd ).    When the process is finished with a socket or file descriptor, it must call ioDregWat( int sd ) to unregister the socket from the wait mechanism and free the allocated wait structure.

How TCP Works, Structurally:
The basic I/O of the infrastructure is currently based on TCP and is viewed from the aspect of opening a server or connecting, as a client, to a server then sending and receiving messages.    The io1 module and it's minions do connection tracking and a lot of exception handling but it is currently TCP/STRM centric. Io1 actually has an ioAddProtocol( int protoNbr, char *pName, pfi_t pInit ) which sets up a protocol_t block and calls the pInit function to initialize the underlying protocol control structures with handlers for snd, rcv, exc, conn, dConn, opnSrv, opnClt, getSd, makSockAddr.    In the case of TCP, ioAddProtocol is passed the address of tcpAddVects() which calls ioDrvInitP() to set up the ioRx and ioTx vectors for TCP read and write.    When ioConn is called it creates a new connection control block and calls ioRegWat(), above.    TCP uses a small 16 byte header which contains a version, message type, dataLength, status, and flags.    Since all I/O is event driven, the type field determines what event will fire when the message arrives.    This provides for in-band and out-of-band data and control of all process and communication functions.    Calls to ioAddCfg( int proto, char *name, char *llc, char *phy, char *cmd, char *key ) populate the IO layer's configuration database.    Once this is done a call to ioOpen( char *name ) will open a server listening for connections, and calls to ioGetPath( char *name ) will attempt to connect to other servers.    An in-bound connection creates a new connection control block with it's sd and links it to the associated port control block.   

If you are using io1 for connection tracking, one additional piece of code must support possible disconnects.    The coder should register a disconnect processor using ioRegEvent( EVTDCONN, pfi_t dConnFun, void *context ).    The call-back prototype is:
dConn( int sd ).