December 19, 2014
Hot Topics:

A Pattern/Framework for Client/Server Programming in Java

  • June 10, 2002
  • By Usman Saleem
  • Send Email »
  • More Articles »

Introduction

Every now and then, we need to build a custom client/server model, especially if we are students and are required to submit some network project using Java's powerful network programming capabilities. In this article I attempt to provide a pattern/framework for developing custom client/server programming using the Java language. This article is divided into two parts: server development and client development.

Server Development

Java's powerful network and multi-tasking (threads) capabilities make it an ideal language for developing servers that can handle multiple clients simultaneously while providing platform independence. You will find many examples of commercial and open source servers built using Java, including famous Web servers such as Tomcat, Jetty, Resin Caucho, and so forth; from chat servers to financial/management servers such as Oracle Financial, and so on.

However, because we are developers, we are often required to build our own client/server model for specific needs. Using Java's built-in capabilities of multi-tasking and Java's network capabilities, we can quickly develop such a server.

Let's start building our server. Following is a UML class diagram for our server:

I am using the Observer pattern to allow low coupling between ClientThread and the Server class. The Server class's startServer method instantiates an inner class called StartServerThread. This class extends the Thread class and its run method listens to all incoming connections. From here, a ClientThread's object is created that implements a runnable interface. Each ClientThread will handle a separate client; its run() method creates a loop for listening to messages from the client; all logic of the server can be implemented from this point onward. Here is the source code for Server class and ClientThread class.

//Server.java
//© Usman Saleem, 2002 and beyond.
//usman_saleem@yahoo.com

package com.usal.serverPattern;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.Observer;
import java.util.Vector;
import java.util.Observable;
import java.io.IOException;
import java.io.*;

public class Server implements Observer {
    private Socket socket;

    /** This vector holds all connected clients.
     * May be used for broadcasting, etc. */
    private Vector clients;
    private ServerSocket ssocket;  //Server Socket
    private StartServerThread sst; //inner class

    /**
     * Represents each currently connected client.
     * @label initiates
     * @clientCardinality 1
     * @supplierCardinality 0..*
     */
    private ClientThread clientThread;

    /** Port number of Server. */
    private int port;
    private boolean listening; //status for listening

    public Server() {
        this.clients = new Vector();
        this.port = 5555; //default port
        this.listening = false;
    }

    public void startServer() {
        if (!listening) {
            this.sst = new StartServerThread();
            this.sst.start();
            this.listening = true;
        }
    }

    public void stopServer() {
        if (this.listening) {
            this.sst.stopServerThread();
		//close all connected clients//

            java.util.Enumeration e = this.clients.elements();
            while(e.hasMoreElements())
            {
		    ClientThread ct = (ClientThread)e.nextElement();
                ct.stopClient();
            }
            this.listening = false;
        }
    }

    //observer interface//
    public void update(Observable observable, Object object) {
        //notified by observables, do cleanup here//
        this.clients.removeElement(observable);
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    

    /** This inner class will keep listening to incoming connections,
     *  and initiating a ClientThread object for each connection. */
    
  private class StartServerThread extends Thread {
        private boolean listen;

        public StartServerThread() {
            this.listen = false;
        }

        public void run() {
            this.listen = true;
            try {

/**The following constructor provides a default number of
 * connections -- 50, according to Java's documentation.
 * An overloaded constructor is available for providing a 
 * specific number, more or less, about connections. */

Server.this.ssocket = 
                      new ServerSocket(Server.this.port);


                   while (this.listen) {
			//wait for client to connect//

                  Server.this.socket = Server.this.ssocket.accept();
                    System.out.println("Client connected");
                    try {
                        Server.this.clientThread = 
                             new ClientThread(Server.this.socket);
                        Thread t = 
                             new Thread(Server.this.clientThread);
                                          Server.this.clientThread.addObserver(Server.this);
                        Server.this.clients.addElement(
                           Server.this.clientThread
                              );
                        t.start();
                    } catch (IOException ioe) {
                        //some error occured in ClientThread //
                    }
                }
            } catch (IOException ioe) {
                //I/O error in ServerSocket//
                this.stopServerThread();
            }
        }

        public void stopServerThread() {
            try {
                Server.this.ssocket.close();
            }
            catch (IOException ioe) {
                //unable to close ServerSocket
            }
            
            this.listen = false;
        }
    }
}

The working of the above Server class is in fact very simple. It simply listens for new clients, gets their socket objects, and passes the information to separate threads; in our case, it is called ClientThread. Following is the source code for the ClientThread class.

//ClientThread.java
//© Usman Saleem, 2002 and Beyond
//usman_saleem@yahoo.com

package com.usal.serverPattern;

import java.net.Socket;
import java.io.PrintWriter;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Observable;

public class ClientThread extends Observable implements Runnable {
    /** For reading input from socket */
    private BufferedReader br;

    /** For writing output to socket. */
    private PrintWriter pw;

    /** Socket object representing client connection */

    private Socket socket;
    private boolean running;

    public ClientThread(Socket socket) throws IOException {
        this.socket = socket;
        running = false;
        //get I/O from socket
        try {
            br = new BufferedReader(
                     new InputStreamReader(
                                           socket.getInputStream()
                                          )
                                   );
            
            pw = new PrintWriter(socket.getOutputStream(), true);
            running = true; //set status
        }
        catch (IOException ioe) {
            throw ioe;
        }
    }
	
    /** 
     *Stops clients connection
     */

    public void stopClient()
    {
        try {
		this.socket.close();
        }catch(IOException ioe){ };
    }

    public void run() {
        String msg = ""; //will hold message sent from client

	//sent out initial welcome message etc. if required...
	try {
        pw.println("Welcome to Java based Server");
      }
      catch(IOException ioe) { }
		

	  //start listening message from client//

        try {
                while ((msg = br.readLine()) != null && running) {
                    //provide your server's logic here//
			
                    //right now it is acting as an ECHO server//

                    pw.println(msg); //echo msg back to client//
                }
                running = false;
            }
            catch (IOException ioe) {
                running = false;
            }
        //it's time to close the socket
        try {
            this.socket.close();
            System.out.println("Closing connection");
        } catch (IOException ioe) { }

        //notify the observers for cleanup etc.
        this.setChanged();              //inherit from Observable
        this.notifyObservers(this);     //inherit from Observable
    }
}

The core of the ClientThread class is its run method, which invokes a loop for getting input from the client. Your logic should start from this point onward. This logic is basically your application protocol. The above mentioned framework/pattern just provides a very generic server implementation. Several enhancements are possible, such as pausing the server, restarting the server, and so on; however, implementing these enhancements is not difficult at all. Two interested methods -- setChanged() and notifyObservers() -- are inherited from the java.util.Observable class; they notify the interested Observer classes that a change has been made. This provides a cleaner approach to decoupling classes, especially in multi-threaded programs.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel