Thursday, May 28, 2009

Creating a Custom Client Server Application in JAVA

I recently had a need to do something a little different from my usual J2EE thing that required me to do a little local networking directly between two clients.  I needed to split a task between two users without creating a bunch of infrastructure.

Since the task was a data entry task, we were using an applet to get some data from a web service, copy that data to the users clipboard, and then call a javascript function to open a web page where the user could paste in the data.  I wanted to split the task so that one user could get the data from the web service and then pass it to another user to do the javascript and pasting part.

If you read this, you will notice right off hand that there are a few things to get past here before you even get to do the client server part.  Number one is that if you are running an applet and want access to the clipboard, you first have to either modify the client policy to allow this, or sign the jar file…I opted to sign the jar file.  Secondly, if you want an applet to run javascript, you have to add the ‘mayscript’ parameter to your applet tag in your html page.  I’m not planning on covering these topics here, but I may blog about them later.

Java Networking

The Client

A client just has to create a new instance of a Socket which has a handle to an input and output stream.  All your client does is write to an InputStream and read the server response from the OutputStream.  This is really quite trivial to do by using a Reader and Writer class.  I used a PrintWriter to write to the server and a BufferedReader to read the response.  I use another BufferedReader to get an input from the command line:

  1: public class SimpleClient
  2: {
  3:     private final Socket clientSocket;
  4:     private enum ServerState{ READY, DONE };
  5:     
  6:     public SimpleTransferClient(String server, int port) throws UnknownHostException, IOException
  7:     {
  8:         clientSocket = new Socket(server, port);
  9:     }
 10: 
 11:     public void start()
 12:     {
 13:         PrintWriter out;
 14:         BufferedReader response;
 15:         BufferedReader commandLine;
 16:         ServerState state = ServerState.READY;
 17:         
 18:         try
 19:         {
 20:             out = new PrintWriter(clientSocket.getOutputStream(), true);
 21:             response = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 22:             commandLine = new BufferedReader(new InputStreamReader(System.in));
 23:             
 24:             String message;
 25:             
 26:             while( ( message = response.readLine() ) != null )
 27:             {
 28:                 state = TransferState.valueOf(message);
 29:                 
 30:                 switch(state)
 31:                 {
 32:                     case READY:
 33:                         out.println(commandLine.readLine());
 34:                         break;
 35:                     default:
 36:                         out.close();
 37:                         response.close();
 38:                         break;
 39:                 }
 40:             }
 41:             
 42:             clientSocket.close();
 43:         }
 44:         catch (IOException e)
 45:         {
 46:             // noop
 47:         }
 48:     }
 49: }
 50: 






Making this work involves creating an instance of the SimpleClient using the server ip address and port in the constructor. 



The Listener



Although a listener is not necessarily required, I thought that it would be better to launch a thread to handle the networking stuff and hand off the request to a server instance.  This way, I could allow multiple clients to connect simultaneously instead of in a queue.



The listener simply creates a ServerSocket and just accepts client requests.  Once it gets a client socket request, it just passes the socket off to a server thread and then goes back to listening for another client.  I took a simplistic approach to this, you may want to limit the number of processes, etc to ensure that you don’t get DDOS’d to death, but in my case, this wasn’t really an issue.



  1: final ServerSocket listener = new ServerSocket(4567);
  2: while(true)
  3: {
  4:     new ServerThread(listener.accept()).start();
  5: }


As I said, this isn’t fancy, but it sets up the service, loops forever and accepts clients as quickly as it can spin off a thread.  Since I was running this inside an applet and wanted some user interaction, I also put this in its own thread.  Obviously, depending on what you are trying to do, this may not be necessary, but this will work in any case.



  1: public final class Listener
  2: {
  3:     private static Thread listenerThread;
  4:     
  5:     /**
  6:      * Static method to start the thread
  7:      */
  8:     public static void start()
  9:     {
 10:         listenerThread = new ListenerThread();
 11:         listenerThread.start();
 12:     }
 13:     
 14:     /**
 15:      * Static method to stop the thread
 16:      */
 17:     public static void stop()
 18:     {
 19:         if ( listenerThread != null )
 20:             listenerThread.interrupt();
 21:     }
 22:     
 23:     /**
 24:      * The actual thread class
 25:      */
 26:     private static class ListenerThread extends Thread
 27:     {
 28:         private boolean listening = false;
 29:         private ServerSocket listener = null;
 30:         
 31:         public void run()
 32:         {
 33:             try
 34:             {
 35:                 listener = new ServerSocket(4567);
 36:                 listening = true;
 37:                 while(listening)
 38:                 {
 39:                     new ServerThread(listener.accept()).start();
 40:                     try
 41:                     {
 42:                         // be nice and sleep once in a while
 43:                         Thread.sleep(1000);
 44:                     }
 45:                     catch (InterruptedException e)
 46:                     {
 47:                         listening = false;
 48:                     }
 49:                 }
 50:                 listener.close();
 51:             }
 52:             catch (IOException e)
 53:             {
 54:                 listening = false;
 55:             }
 56:             
 57:         }
 58:         
 59:         public void interrupt()
 60:         {
 61:             try
 62:             {
 63:                 if ( listener != null )
 64:                     listener.close();
 65:             }
 66:             catch (IOException e)
 67:             {
 68:                 // noop
 69:             }
 70:             
 71:             listening = false;
 72:         }
 73:     }
 74: }


So then firing up the listener would just be a static call to Listener.start();



The Server



In order to make sure that client requests don’t get queued up, we implement the actual server as a thread as well.  This way, when a client connects, the listener creates a new ServerThread and then goes back to listening.



The server is almost identical to the client in terms of code.  Both sides use a plain old Socket and read and write from an output stream.  I used a PrintWriter for my output and a BufferedReader for my input:



  1: public class ServerThread
  2: {
  3:     private final Socket clientSocket;
  4:     
  5:     private enum ServerState{ READY, DONE };
  6: 
  7:     public ServerThread(Socket socket)
  8:     {
  9:         super("ServerThread");
 10:         clientSocket = socket;
 11:     }
 12: 
 13:     public void run()
 14:     {
 15:         PrintWriter output;
 16:         BufferedReader response;
 17:         
 18:         try
 19:         {
 20:             output = new PrintWriter(clientSocket.getOutputStream(), true);
 21:             response = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 22:             
 23:             ServerState state = ServerState.READY;
 24:             String message;
 25:             output.println(state.toString());
 26:             
 27:             while(! state.equals(ServerState.DONE) && (message = response.readLine()) != null )
 28:             {
 29:                 if ( "quit".equalsIgnoreCase(message) )
 30:                     state = ServerState.DONE;
 31: 
 32:                 output.println("You Said: " + message);
 33:                 output.println(state.toString());
 34:             }
 35:             output.println("Closing Connection...");
 36:             output.close();
 37:             response.close();
 38:             clientSocket.close();
 39:         }
 40:         catch (IOException e)
 41:         {
 42:             // noop
 43:         }
 44:     }
 45: }

No comments: