SDSU CS 596: Client Server Programming
Buffering and State Machines

San Diego State University -- This page last updated March 8, 1995


I/O Buffering

Async I/O using select()

Basic rules:

Both input and output should be buffered.

Input needs to be split up into lines or records.

Output needs to be converted into a byte stream.


Review of Return Values

Return values for read() and write()

read():

>0
# of bytes read
0
End of file. Connection closed
<0
Error condition

write():

>=0
# of bytes actually written
<0
Error condition

Error codes that need special handling:


Program Structure

For input:


Program Structure

For output:


Program Structure Overall

setup;
for ever
{
    clear readfds and writefds;
    for each client
    {
        set bits in readfds and writefds;
    }
    set readfds bit for passive socket;
    result = select(...)
    for each client
    {
        if bits set in readfds or writefs
            deal with it;
    }
    if bit for passive socket is set,
        accept new connection;
}

Input Buffering

Two interfaces to the buffer:

Select interface caused the buffer to call read() and fill its internal holding area.

Once a complete record is available, it needs to let the protocol engine know.

The protocol engine will then request records.


Input Buffering API (example)

void DataAvailable(int fd)Tells the buffer that it needs to call read() on the given filedescriptor.

int HasRecord()Is TRUE if at least one complete record is available.

Record GetRecord()Returns one record.


Output Buffering API (example)

FlushBuffer(int fd)Tells the buffer that it is allowed to write data to the file descriptor.

WriteToClient(Record someRecord)Writes a complete record into the buffer.

int IsEmpty()Returns TRUE if the output buffer is empty.


State Machines

Use a state machine to process your protocol

You are NOT required to be fluent in Finite Automata theory to use state machines!

Sample protocol: SPOP (Simple POP)

Commands allowed:


State Machine Implementation

Two general methods:

Table:


              1          2         3         
4        5    
USER          2          5         5         4        5    
PASS          5          3         5         4        5    
LIST          5          5         3         4        5    
QUIT          4          4         4         4        4    

Switch

switch (state)
    case 1:
        switch (input)
            case USER: state = 2;
            case QUIT: state = 4;
            default: state = 5;
    case 2:
        switch (input)
            case PASS: state = 3;
            case QUIT: state = 4;
            default: state = 5;
    case 3:
        switch (input)
            case LIST: state = 3;
            case QUIT: state = 4;
            default: state = 5;
    case 4:
        state = 4;
    case 5:
        switch (input)
            case QUIT: state = 4;
            default: state = 5;

Actions

A state machine would do not good without some actions.

The default action for any given state and input is to go to the state specified by the machine.

Actions can determine which state to pick next:

In POP the username and password need to be verified:


Realistic implementation

typedef struct
{
    char *command;
    int (*function)();
    int valid_in;
    int win;
    int lose;
} vector;
vector switchtable[] =
{
    {"user", user, AUTH1, AUTH2, AUTH1},
    {"pass", pass, AUTH2, TRANS, AUTH},
        .
        .
        .
    {NULL, NULL, 0, 0, 0}
};