Client Server
Navigate Concurrent Programming topic: ) |
In 1990s, the trend was moving away from Mainframe computing to Client/Server as the price of Unix servers dropped. The database access and some business logic were centralized on the back-end server, collecting data from the user program was installed on the front-end users' "client" computers. In the Java world there are three main ways the front-end and the back-end can communicate.
- The client application uses JDBC (Java DataBase Connectivity API) to connect to the data base server, (Limited business logic on the back-end, unless using Stored procedures).
- The client application uses RMI (Remote Method Invocation) to communicate with the back-end.
- The client application uses a socket connection to communicate with the back-end.
Socket Connection Example
editThis page shows an example of a socket connection.
Create a Server
editThe Java language was developed having network computing in mind. For this reason it is very easy to create a server program. A server is a piece of code that runs all the time listening on a particular port on the computer for incoming requests. When a request arrives, it starts a new thread to service the request.
See the following example:
Listening on a port
edit- ComServer
- class is for listening on a port for a client.
Code listing 1.1: ComServer
import java.net.ServerSocket;
/**
* -- Main Server Class; Listening on a port for client; If there is a client,
* starts a new Thread and goes back to listening for further clients. --
*/
public class ComServer
{
static boolean GL_listening = true;
/**
* -- Main program to start the Server --
*/
public static void main(String[] args) throws IOException
{
ComServer srv = new ComServer();
srv.listen();
} // --- End of Main Method ---
/**
* -- Server method; Listen for client --
*/
public int listen() throws IOException
{
ServerSocket serverSocket = null;
int iPortNumber = 9090;
// --- Open the Server Socket where this should listen ---
try {
System.out.println( "*** Open the listening socket; at:"+ iPortNumber + " ***" );
serverSocket = new ServerSocket( iPortNumber );
} catch (IOException e) {
System.err.println("Could not listen on port:"+iPortNumber );
System.exit(1);
}
while ( GL_listening )
{
ComServerThread clientServ;
// --- Listening for client; If there is a client start a Thread -
System.out.println( "*** Listen for a Client; at:"+ iPortNumber + " ***" );
clientServ = new ComServerThread( serverSocket.accept() );
// --- Service a Client ---
System.out.println( "*** A Client came; Service it ***" );
clientServ.start(); /* --- Use for multy Threaded --- */
// clientServ.run(); /* --- Use for Single Threaded --- */
}
// --- Close the Server socket; Server exiting ---
serverSocket.close();
return 0;
} // --- End of listen Method ---
} // --- End of ComServer Class ---
|
- ServerSocket( iPortNumber )
- Creates a server socket, bound to the specified port.
- serverSocket.accept()
- Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made. It returns a new Socket.
Service One Client
edit- ComServerThread
- This class extended from a Thread is responsible to service one client. The Socket connection will be open between the client and server. A simple protocol has to be defined between the client and server, the server has to understand what the client wants from the server. The client will send a terminate command, for which the server will terminate the socket connection. The ComServerThread class is responsible to handle all client requests, until the client sends a terminate command.
Code listing 1.2: ComServerThread
/**
* -- A class extended from a Thread; Responsible to service one client --
*/
class '''ComServerThread''' extends Thread
{
private Socket clientSocket = null;
COM_DATA tDataFromClient;
COM_DATA tDataToClient;
ObjectInputStream oIn;
ObjectOutputStream oOut;
/**
* -- Constructor --
*/
public ComServerThread( Socket socket )
{
super( "ComServerThread" );
this.clientSocket = socket;
} // -- End of ComServerThread() constructor --
/**
* -- Overrun from the Thread (super) class --
*/
public void run()
{
try {
// --- Create the Writer; will be used to send data to client ---
oOut = new ObjectOutputStream( clientSocket.getOutputStream() );
// --- Create the Reader; will be used to get data from client ---
oIn = new ObjectInputStream( clientSocket.getInputStream() );
// --- Create a new protocol object ---
ComProtocol comp = new ComProtocol();
// --- Send something to client to indicate that server is ready ---
tDataToClient = '''comp.processInput( null );'''
'''sendDataToClient'''( tDataToClient, oOut );
// --- Get the data from the client ---
while ( true )
{
try {
tDataFromClient = '''getDataFromClient( oIn )''';
// --- Parse the request and get the reply ---
tDataToClient = '''comp.processInput( tDataFromClient );'''
// --- Send data to the Client ---
'''sendDataToClient'''( tDataToClient, oOut );
}
catch ( EOFException e ) {
System.out.println( "Client Disconnected, Bye, Bye" );
break;
}
// --- See if the Client wanted to terminate the connection ---
if ( tDataToClient.bExit )
{
System.out.println( "Client said Bye. Bye" );
break;
}
}
// --- Close resources; This client is gone ---
comp.Final();
oOut.close();
oIn.close();
clientSocket.close();
} catch ( IOException e ) {
e.printStackTrace();
}
} // -- End of run() Method --
/**
* Get data from Client
*/
private static COM_DATA '''getDataFromClient'''( ObjectInputStream oIn ) throws IOException
{
COM_DATA tDataFromClient = null;
// --- Initialize variables ---
// tDataFromClient = new COM_DATA();
while ( tDataFromClient == null )
{
try {
// --- Read Line Number first --
tDataFromClient = (COM_DATA) oIn.readObject();
} catch ( ClassNotFoundException e ) {
System.out.println( "ClassNotFound" );
}
}
System.out.println( "Get: " + tDataFromClient.comData );
return tDataFromClient;
} // --- getDataFromClient() Method ---
/**
* Send data to Client
*/
private static void '''sendDataToClient'''( COM_DATA tDataToClient,
ObjectOutputStream oOut ) throws IOException
{
System.out.println( "Sent: " + tDataToClient.comData );
oOut.writeObject( tDataToClient );
return;
} // -- End of sendDataToClient() Method --
} // --- End of ComServerThread class ---
|
- COM_DATA tDataFromClient
- This variable will contain the data object from the client.
- COM_DATA tDataToClient
- This variable will contain the data object to be sent to the client.
- sendDataToClient
- This method sends the data object to the client.
- getDataFromClient
- This method gets the data object from the client.
- processInput( tDataFromClient )
- This method of the class
ComProtocol
interprets the client commands and returns the data object that will be sent back to the client.
Handling the request; implements the communication protocol
edit- ComProtocol
- This class implements, and encapsulates the communication logic (protocol). The protocol is the following:
- The client initiate the connection.
- The server accepts it and sends an acknowledgment notifying that it's ready
- The client sends a request
- The server response based on the request
- ...
- The client sends a
BYE
request - The server acknowledge the
BYE
request and disconnects the socket connection - The client gets the acknowledgment to the
BYE
- The client sends a
- ...
- The client sends a
SHUTDOWN
request - The server acknowledge the
SHUTDOWN
request and disconnects and also stops listening of other clients. - The client gets the acknowledgment to the
SHUTDOWN
- The client sends a
Code listing 1.3: ComProtocol
class '''ComProtocol'''
{
private static final int COM_STATUS_WAITING = 0;
private static final int COM_STATUS_READY_SENT = 1;
private static final int COM_STATUS_DATA_SENT = 2;
private static final int COM_STATUS_WAITING_FOR_TERMINALID = 3;
private int state = COM_STATUS_WAITING;
// --- Reference to 'BACK-END' module ---
private MqTeAccess mqTe;
...
/**
* Create a protokol object; CAll MQ INI function
*/
public ComProtocol()
{
int iRet = 0;
// --- Initialize 'BACK-END' modules ---
mqTe. ...
...
}
/**
* --- Process the Input and Create the output to the Client ---
*/
public COM_DATA processInput( COM_DATA theInput )
{
COM_DATA theOutput;
// --- Initialize Variables ---
theOutput = new COM_DATA();
// --- Check if the Clients want to disconnect ---
if ( theInput != null )
{
if ( theInput.comData.equals('''"!BYE.@"''') )
{
// --- The Client wants to terminate; Echo data back to client
theOutput.comData = "BYE.";
// --- Mark the communication to be terminated ---
theOutput.bExit = true;
// --- Set the internal state to wait for a new client ---
state = COM_STATUS_WAITING;
// --- Return Data object to be sent to the client ---
return theOutput;
}
if ( theInput.comData.equals('''"!SHUTDOWN.@"''') )
{
// --- The Client wants to terminate; Echo data back to client
theOutput.comData = "BYE.";
// --- Mark the communication to be terminated ---
theOutput.bExit = true;
// --- Tell the server to stop listening for new clients ---
ComServer.GL_listening = false;
// --- Set the internal state to wait for a new client ---
state = COM_STATUS_WAITING;
// --- Return Data object to be sent to the client ---
return theOutput;
}
}
if ( state == COM_STATUS_WAITING )
{
// --- Send ready Message to the Client ---
theOutput.comData = "Ready:";
// --- Set the internal state ready; and wait for TerminalId ---
state = COM_STATUS_WAITING_FOR_TERMINALID;
}
else if ( state == COM_STATUS_WAITING_FOR_TERMINALID )
{
int iRet;
// --- Get the Terminal ID ---
sTermId = theInput.comData;
// --- Call 'BACK-END' modules ... ---
mqTe. ...
...
// --- Send ready Message with the Server Version to the Client ---
theOutput.comData = "Ready;Server Version 1.0:";
// --- Set the internal state raedy; and wait for TerminalId ---
state = COM_STATUS_READY_SENT;
}
else if ( state == COM_STATUS_READY_SENT )
{
int iRet;
String sCommand = theInput.comData;
// --- Call 'BACK-END' modules ...
...
/*
** --- Check if we should get Response data ---
*/
if ( theInput.iRet == COM_DATA.NOWAIT_FOR_RESPONSE ) {
// -- Set the Output Value ---
theOutput.iRet = iRet;
theOutput.comData = "";
}
else {
// --- Call 'BACK-END' modules ---
mqTe. ...
// --- Set the Output Value ---
theOutput.comData = mqTe.sResponseBuffer;
theOutput.iRet = iRet;
}
}
return theOutput;
} // --- End of Method processInput() ---
} // --- End of ComProtocol Class Definition ---
----
|
The Data object that goes through the network
edit- COM_DATA
- is data structure class that is transmitted through the network. The class contains only data.
Code listing 1.4: COM_DATA
/**
* COM_DATA data structure
*/
public class COM_DATA implements Serializable
{
public String comData;
public boolean bExit;
public int iRet;
/**
* --- Constants values can be passed in in iRet to the Server ---
*/
static final int WAIT_FOR_RESPONSE = 0;
static final int NOWAIT_FOR_RESPONSE = 1;
/**
* Initialize the data structure
*/
public COM_DATA()
{
comData = "";
bExit = false;
iRet = 0;
} // -- End of COM_DATA() Constructor --
/**
* Copy over it contents
*/
public void copy( COM_DATA tSrc )
{
this.comData = tSrc.comData;
this.bExit = tSrc.bExit;
this.iRet = tSrc.iRet;
return;
}
} // -- End of COM_DATA class --
|
Create the Client
editA client code for a server/service is usually an API that a user application uses to interface to the server. With the help of a client API the user application does not have to know how to connect to the server to get services.
- ComClient
- This class is the client API. The application is using this class to communicate with the server.
The following is the client class for the above server:
Code listing 1.5: ComClient
public class ComClient
{
private Socket comSocket;
private ObjectOutputStream oOut;
private ObjectInputStream oIn;
private boolean IsItOpen = false;
/**
* --- Open Socket ---
*/
public void openCom( String sServerName,
int iPortNumber ) throws UnknownHostException,
IOException
{
try {
// --- Open Socket for communication ---
comSocket = new Socket( sServerName, iPortNumber );
// --- Get Stream to write request to the Server ---
oOut = new ObjectOutputStream( comSocket.getOutputStream() );
// --- Get Stream// to read from the Server
oIn = new ObjectInputStream( comSocket.getInputStream());
// --- Set internal Member variable that the Communication opened ---
IsItOpen = true;
} catch ( java.net.UnknownHostException e ) {
System.err.println( "(openCom:)Don't know about host: "+sServerName );
IsItOpen = false;
throw( e );
} catch ( java.io.IOException e ) {
System.err.println("(openCom:)Couldn't get I/O for the connection to: "+ sServerName );
IsItOpen = false;
throw( e );
}
}
/**
* --- Check if Socket is open ---
*/
public boolean isItOpen()
{
return IsItOpen;
}
/**
* --- Get data string from the Server ---
*/
public void getServerData( COM_DATA tServData ) throws IOException
{
// --- Initialize Variables ---
tServData.comData = "";
// --- Get the Response from the Server ---
try {
tServData.copy( (COM_DATA) oIn.readObject() );
}
catch ( ClassNotFoundException e ) {
System.out.println( "Class Not Found" );
}
System.out.println( "Server: " + tServData.comData );
if ( tServData.comData.equals("BYE.") )
{
tServData.bExit = true;
}
return;
}
/**
* --- Send data to the Server ---
*/
public void sendDataToServer( COM_DATA tServData ) throws IOException
{
// --- Send the data string ---
System.out.println( "Send: " + tServData.comData );
oOut.writeObject( tServData );
return;
}
/**
* --- Close Socket ---
*/
public void closeCom() throws IOException
{
oOut.close();
oIn.close();
comSocket.close();
IsItOpen = false;
}
}
|
- getServerData( COM_DATA tServData )
- This method reads the data from the server and copies the values to
tServData
object. - sendDataToServer( COM_DATA tServData )
- This method sends the
tServData
object through the network to the server. - oIn.readObject()
- This method returns the data object sent by the server.
- oOut.writeObject( tServData )
- This method sends the data object to the server.