SDSU CS 596: Client Server Programming
State, Classes, Modularity

[To Lecture Notes Index]
San Diego State University -- This page last updated March 20, 1995
----------

Contents of State, Classes, Modularity Lecture

  1. State Machine Implementation
    1. Switch
    2. Table
    3. State Classes
      1. First Pass
      2. Storing Information
      3. Reducing Time Spent Creating State Objects
      4. Parsing the Input Part 1
      5. Parsing the Input Part 2 - Who reads input?
  2. Modularity
    1. Sample Main Program

State Machine Implementation


Three general methods:
Switch
Table
State Classes


Sample protocol: SPOP (Simple POP)

Commands allowed:
USER
PASS
LIST
QUIT



Switch


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

Table


Input State
	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	


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}
};

State Classes


First Pass

SPop Class
class SPop {
public:
	SPop();
	void USER();	void PASS();	
	char* LIST();	void QUIT();

private:
	friend class SPopState;
	void ChangeState (SPopState* NewState)

	SPopState*	CurrentState;
};

SPop :: SPop () {
	CurrentState = new Start();
}
void SPop :: ChangeState (SPopState* NewState) {
	CurrentState = NewState;
}

void SPop :: USER () {
	CurrentState -> USER( this )
}

void SPop :: PASS () {
	CurrentState -> PASS ( this )
}

char* SPop :: LIST () {
	return CurrentState -> LIST ( this )
}

void SPop :: QUIT () {
	CurrentState -> QUIT ( this )
}


SPopState

Define the default operations for each input
class SPopState {
public:
	virtual void USER ( SPop* Context );
	virtual void PASS ( SPop* Context );
	virtual char* LIST ( SPop* Context );
	virtual void QUIT ( SPop* Context );
}

void SPopState  :: USER ( SPop* Context )	{
	Context -> ChangeState( new Error() );
}

void SPopState  :: PASS ( SPop* Context )	{
	Context -> ChangeState( new Error() );
}

void SPopState  :: LIST ( SPop* Context )	{
	Context -> ChangeState( new Error() );
}

void SPopState  :: QUIT ( SPop* Context )	{
	Context -> ChangeState( new Quit() );
}


The Real State Classes
Start
class Start : public SPopState {
public:
	virtual void USER ( SPop* Context );
}

void Start :: USER ( SPop* Context )	{
	Context -> ChangeState( new Authenticate() );
}

Authenticate
class Authenticate : public SPopState {
public:
	virtual void PASS ( SPop* Context );
}

void Authenticate :: PASS ( SPop* Context )	{
	Context -> ChangeState( new List() );
}

List
class List : public SPopState {
public:
	char* List ( SPop* Context );
}

char* List :: List ( SPop* Context )	{
	Get and return list
}
Error

class Error : public SPopState {
	void QUIT ( SPop* Context );
}

void Error :: QUIT ( SPop* Context )	{
	Context -> ChangeState( new Quit() );
}


Quit
class Error : public SPopState {
	void QUIT ( SPop* Context );
}

Get Real This Doesn't Do Anything!

How does this interface with the program?
Where does the input come from?
Who parses the input?
Where does the output go?
What if different states can accept different inputs?
How do the state classes store information (user name)?
How does one "end the protocol"?
Don't you create a lot of state objects needlessly?

Storing Information


Example: User name and password
class SPop { 
public:
	SPop();	void USER();	void PASS();	
	char* LIST();	void QUIT();

private:
	char* User;
	char* Password;

	friend class SPopState;
	void ChangeState (SPopState* NewState)

	SPopState*	CurrentState;
};
class Start : public SPopState {
public:
	virtual void USER (char* UserName, SPop* Context );
}

void Start :: USER (char* UserName, SPop* Context )	{
	Context -> User = UserName;
	Context -> ChangeState( new Authenticate() );
}

Reducing Time Spent Creating State Objects

When switching states, just access correct state

then can use only one object per state class in a program

class Start : public SPopState {
public:
	static SPopState* Instance();
	void USER ( SPop* Context );

protected:
	Start();

private:
	static SPopState* _instance;
}

SPopState*  Start :: _instance = 0;

SPopState*  Start :: Instance() {

	if (_instance == 0) _instance = new Start();
	return _instance;
}

Parsing the Input Part 1


In SPop, input is read from an external source



Input must be translated from string to commands
if (Input == "USER")	x -> USER()

else if (Input == "PASS")	x -> PASS()

else if (Input == "LIST")	x -> LIST()


Who parses?
Driver program
Not recommended, violates information hiding
SPop
Perhaps the logical place
SPopState, Start, Error, ...
If different states have lots of different commands
If you are going to add lots of states
Makes harder for SPop and state classes to interact

Parsing using SPopState and Children

class SPopState {
public:
	virtual void input( char* Message, SPop* Context) = 0;

	// Common commands
	virtual void QUIT ( SPop* Context );
	virtual void Error ( SPop* Context );
}


void SPopState  :: LIST ( SPop* Context )	{
	Context -> ChangeState( new Error() );
}


void SPopState  :: QUIT ( SPop* Context )	{
	Context -> ChangeState( new Quit() );
}
Start
class Start : public SPopState {
public:
	virtual void USER (char* Name, SPop* Context );
	virtual void input( char* Message, SPop* Context);

}


void Start :: input( char* Message, SPop* Context) {

	if (Message starts with "USER")
		USER(last part of Message, Context)
	else if (Message starts with "QUIT")
		QUIT(Context)
}
	
void Start :: USER (char* Name, SPop* Context )	{
	Context -> User = Name;
	Context -> ChangeState( Authenticate::Instance() );
}


Parsing without if statements
Only for the C++ crazies

One does not really want to parse commands of a protocol with if statements

Use a Dictionary (an container that can index items on strings)


// Set up
Dictionary < string, (SPopState ::*) ( *SPop ) > Commands;

Commands["USER"] = SPop :: User( *SPop );


//Usage

string Message 

read( Message )

Commands[ first part of message](second part of message)

Parsing the Input Part 2 - Who reads input?



Driver Program
SPop  Protocol;

while ( SPop.state() =! "Quit" ) {
	readn( ClientSocket , message ) ;
	response = SPop.input ( message );
	if ( response =! null ) writen( ClientSocket , response );
}


SPop
SPop  Protocol;

Protocol.interactWithClient( ClientSocket );


Problems
inetd
IO is via standard in and standard out
single-process, concurrent server
select must drive io

Modularity


Modules (functions, classes, etc.) should
do one conceptual thing
be at the same level of detail
hide information

Modules should be created around data not steps in the process


Each module should be characterized by design decisions that it hides from other modules


Some server code / library issues:
inetd vs. command line startup
stateless verses stateful protocol
single verses multiple process concurrency
UDP verses TCP

Sample Main Program


int main(int argc; char* argv[]) {

	CommandLineOption	ServerOptions(argc, argv);
	
	if ( ServerOptions.containsOneOfFlags( "ip" ) =! TRUE) {
		write error message;
		exit();
	};
	
	if ( ServerOptions.containsFlag( "i") == TRUE )	{// inetd
	
		respondToResquestOn(cin, cout);
		}
	
	else {
	
		int PortNumber;
		int ConvertError;
		
		ConvertError = stringToInt( ServerOptions.flagValue("p"), 
										PortNumber);
		
		assert( ConvertError =! -1);
		
		runConcurrentServerOn(PortNumber);
	}
}