SDSU CS 596 Client-Server Programming
State Example

[To Lecture Notes Index]
San Diego State University -- This page last updated April 30, 1996
----------

Contents of State Example Lecture

  1. Using States - SPOP Example
    1. The Big Picture
    2. The classes
      1. SPOPServer
      2. SPOPHandler
      3. SPOP_Command
      4. SPOP_Response
      5. SPOPOutputStream
      6. SPOPInputStream
      7. SPOPState
      8. NoAuth
      9. HaveUser

Using States - SPOP Example


Simple Post Office Protocol (SPOP)

Command have the same functionality as POP but are limited to the following:
USER <username>
PASS <password>
LIST
RETR <message number>
QUIT

Will assume that <username>, <password>, <message number> are strings with no spaces.

All commands end with crlf

Responses are as in POP3

See notes Parsing and State machines for details on states and commands
Naming the states

We will use the following names for the states:
NoAuth
HaveUser
Process
Invalid
Quit

Classes Used

SPOPState
Parent for all state classes


NoAuth, HaveUser, Process, Invalid, Quit
State classes


SPOPHandler
Gets messages and sends them to current state


SPOP_Command, SPOP_Response
Internal representation of client commands and server responses


SPOPServer
Network interface

SPOPInputStream, SPOPOutputStream

The Big Picture



Please note the following code is conceptual

The classes


SPOPServer

import java.net.*;
import java.io.*;
import sdsu.io.ASCIIInputStream;

class SPOPServer 
	{
	static int  portNumber  =  4444;

	public static void main(String args[]) 
		{
		ServerSocket serverSocket;
		serverSocket = serverSocketOn( portNumber );

		Socket clientSocket = null;
		InputStream  fromClient;
		OutputStream  toClient;

		while ( true )
			{
			clientSocket  =  acceptClientRequestOn( serverSocket );

			fromClient =  inputStreamFromClient( clientSocket  );

			toClient  =  outputStreamToClient( clientSocket  );

			processClientRequest( fromClient, toClient  );
			}
		}

	protected  static  ServerSocket  serverSocketOn( int portNumber )
		{
			ServerSocket serverSocket = null;

		try 
			{
			serverSocket = new ServerSocket( portNumber );
			} 
		catch (IOException e) 
			{
			System.out.println("Could not listen on port: "  + ", " + e);
			System.exit(1);
			}
		return serverSocket;
		}

	protected static Socket acceptClientRequestOn( ServerSocket server)
		{
		Socket clientSocket = null;
		try 
			{
			clientSocket = server.accept();
			} 
		catch (IOException e) 
			{
			System.out.println("Accept failed: "  + ", " + e);
			System.exit(1);
			}
		return clientSocket;
		}


	protected static InputStream inputStreamFromClient( Socket client )
		{
		InputStream  inputSocketStream;
		BufferedInputStream  bufferedInputSocketStream = null;

		try 
			{
			inputSocketStream  = client.getInputStream();

			bufferedInputSocketStream  = new BufferedInputStream(
											inputSocketStream  );

			}
		catch  ( IOException e)
			{
			// What to do here?
			}
			return  bufferedInputSocketStream;
		}


	protected static OutputStream outputStreamToClient( Socket client )
		{
		OutputStream  outputSocketStream;
		BufferedOutputStream  bufferedOutputSocketStream = null;

		try
			{
			outputSocketStream  = client.getOutputStream();

			bufferedOutputSocketStream  = new BufferedOutputStream(
									outputSocketStream, 1024  );
			}
		catch  ( IOException e)
			{
			// What to do here?
			}
		return  bufferedOutputSocketStream;
		}

	protected static void processClientRequest( InputStream in, 
												OutputStream out )
		{
		SPOPHandler  mail  =  new  SPOPHandler( in, out);
		Thread  serverThread  =  new  Thread( mail, "Mail Server" );
		serverThread.start();
		}


SPOPHandler

public class SPOPHandler
	{
	protected SPOPState currentState = new NoAuth( );
	
	protected SPOPInputStream cin;
	protected SPOPOutputStream cout;

	public SPOPHandler( InputStream in, OutputStream out )
		{
		cin = new SPOPInputStream( cin );
		cout = new SPOPOutputStream( cout );
		}

	public void run( )
		{
		while !( currentState instanceof Quit )
			{
			SPOP_Command clientRequest;
			SPOP_Response serverResponse;

			cin.read( clientRequest );
			currentState = getServerResponse( clientRequest , 
												serverResponse );
			cout.write( serverResponse );
			}

		// In quit state, close connection
		cin.close;
		cout.close;
		}

	protected SPOPState getServerResponse( 
								SPOP_Command request,
								SPOP_Response serverResponse)
		{
		SPOPState nextState;
		String  command = request.command();

		if ( command.compareTo( "USER" ) == 0 )
			nextState = currentState.USER( request, serverResponse );

		else if ( command.compareTo( "PASS" ) == 0 )
			nextState = currentState.PASS( request, serverResponse );

		else if ( command.compareTo( "LIST" ) == 0 )
			nextState = currentState.LIST( request, serverResponse );

		else if ( command.compareTo( "QUIT" ) == 0 )
			nextState = currentState.QUIT( request, serverResponse );
		
		else 
			{
			//create an error message here
			serverResponse = error messsage
			}

		return nextState;
		}
	}

SPOP_Command

public class SPOP_Command
{
	protected static final String EndOfCommand = "\r\n";
	protected static final String Separator = " ";

	protected String command = " "; // avoid null string 
	protected String argument = " ";  // problems

	public SPOP_Command( String aCommand )
	{
		command = aCommand ;
	}

	public setArgument( String  SpopArgument)
	{
		argument = SpopArgument;
	}

	public String command()
	{
		return command;
	}

	public String argument()
	{
		return argument;
	}

	// Produce a string representation of command object
	public String toString()
	{
		return command() + Separator + 
				argument() + EndOfCommand;
	}

	// return a command object from string represention of 
	// command
	public static SPOP_Command fromString( String message )
	{
		StringTokenizer messageParser;
		messageParser = new StringTokenizer( message, Separator );
		
		String commandPart = messageParser.nextToken();
		String argumentPart = messageParser.nextToken();

		SPOP_Command commandFromString;
		commandFromString = new SPOP_Command(
											commandPart ); 
		commandFromString.argument( argumentPart );

		return commandFromString;

}


SPOP_Response

public class SPOP_Response
{
	// See POP3 for these constants
	protected static final String EndOfCommand = "\r\n.\r\n";
	protected static final String Separator = "\r\n";

	protected String statusMessage = " "; 
	protected String data = " ";  

	public SPOP_Response( String status )
	{
		statusMessage = status ;
	}

	public setData( String  information)
	{
		data = information;
	}

	public String status()
	{
		return statusMessage ;
	}

	public String data()
	{
		return data;
	}

	// Produce a string representation of response object
	public String toString()
	{
		return status() + Separator + data() + EndOfCommand;
	}

	// return a command object from string represention of 
	// command
	public static SPOP_Command fromString( String message )
	{
		StringTokenizer messageParser;
		messageParser = new StringTokenizer( message, Separator );
		
		String statusPart = messageParser.nextToken();
		String dataPart = messageParser.nextToken();

		SPOP_Response responseFromString;
		responseFromString = new SPOP_Response( statusPart ); 
		responseFromString.argument( dataPart );

		return responseFromString;
}

SPOPOutputStream


class SPOPOutputStream extends OutputStream
	{
	PrintStream cout;

	public SPOPOutputStream( OutputStream out )
		{
		cout = new PrintStream( out );
		}

	public void print( SPOP_Command aCommand)
		{
		String message = aCommand.asString();
		cout.print( message );
		cout.flush();
		}

	public void print( SPOP_Response aResponse )
		{
		String message = aResponse.asString();
		cout.print( message );
		cout.flush();
		}

	}


SPOPInputStream

class SPOPInputStream extends InputStream
	{
	ASCIIInputStream cin;

	public SPOPInputStream( InputStream in )
		{
		cin = new ASCIIInputStream( in );
		}

	// Read a command from client, translate to command object
	public SPOP_Command readSPOP_Command()
		{
		String fromClient = cin.readLine();

		SPOP_Command commandFromClient;
		commandFromClient = SPOP_Command.fromString(
													fromClient );
		return commandFromClient ;
		}

	// Read a response from server, translate to command object
	public SPOP_Response  readSPOP_Response()
		{
		// Like readSPOP_Command but harder
		}

	}




SPOPState


public class SPOPState {
	public SPOPState USER( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		serverResponse = new SPOP_Response( "-ERR" );
		return new Invalid();
	}

	public SPOPState PASS( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		serverResponse = new SPOP_Response( "-ERR" );
		return new Invalid();
	}

	public SPOPState LIST( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		serverResponse = new SPOP_Response( "-ERR" );
		return new Invalid();
	}

	public SPOPState RETR( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		serverResponse = new SPOP_Response( "-ERR" );
		return new Invalid();
	}

	public SPOPState QUIT( SPOP_Command clientRequest,
								SPOP_Response serverResponse	){
		serverResponse = new SPOP_Response( "-ERR" );
		return new Invalid();
	}
}

NoAuth

public class NoAuth extends SPOPState
{

	public SPOPState USER( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		serverResponse = new SPOP_Response( "+OK" );
		String userName = clientRequest.getData();
		return new HaveUser( userName );
	}
}



HaveUser

public class HaveUser extends SPOPState
{
	protected String userName;

	public HaveUser( String name )
		{
		userName = name;
		}

	public SPOPState PASS( SPOP_Command clientRequest,
								SPOP_Response serverResponse	)
	{
		String password = clientRequest.getData();

		put some code here that verifies that userName and password
		form a valid login

		if valid login then
			{
			serverResponse = new SPOP_Response( "+OK" );
			return new Process( );
			}

		else {
			serverResponse = new SPOP_Response( "+ERR" );
			return new Invalid( );
			}
	}
}

----------