Some of the general characteristics of clients and server applications are, in general, client applications have the following characteristics:
They are applications that become a client temporarily when remote acces is needed, but perform other computation locally.
They are invoked by a user and execute for one session.
They run locally on the user's local computer.
They actively initiate contact with a server and so must know the server details.
They can access multiple services as required.
In general, server applications have the following characteristics:
They are special-purpose applications dedicated to providing one service.
They are invoked automatically when a system boots, and continue to execute through many sessions.
They generally run on a shared computer.
They wait passively for contact from remote clients.
They accept contact from clients, but offer a single service.
Note that the word server here is referring to a piece of software. However, a computer running one or more server applications is often also referred to as a server.
We will now write a client/server application pair where:
The Client will take some data from the user and validate it (as in Finger)
The Client will then send this data to the Server for processing (as in Finger)
The Server will process the data and return the results
The Client will receive the results and display the results in some form.
For our example here We will do this by building a Date/Time Service. The client will simply call the server and ask it for the current date and time.
The Date Server is possibly the most complex part of this application, in this case consisting of three main classes:
The DateTimeService
class - A basic class to get the current
date and time when it is called
The HandlerConnection
class - This class is used to handle any
requests that arrive to the DateServer. This class is specially designed to allow a later transition
to a threaded server.
The DateServer
class - This is the main server application, that
simply listens for connections and creates a HandlerConnection
object when
a connection occurs to the server
We will choose the port 5050 as the port to which we send and listen to for our service. Both a client and a server need to create their own sockets. On the server we listen to port 5050.
The Date Client is very similar to the Finger client we looked at in the section called “The Finger Client” except that the data transfer uses an Object Stream to send and receive data - we will discuss this later.
We will first discuss what occurs in the client/server application before looking directly at the code.
First off, the server is started by typing the command java DateServer on the physical
server machine. The DateServer
starts and begins listening for connections on port 5050.
In Figure 10.6, “The Date Client Creates a Connection to the Date Server.” the DateClient
class is executed by using the
command java DateClient theServer where theServer is the hostname of the physical
machine on which the DateServer
class is located. The DateClient
creates a socket and connects to the DateServer
server socket running at port
5050. The DateServer
should accept this connection provided it is not busy.
Next, as in Figure 10.7, “The Date Server creates a HandleConnection object.” the DateServer
class creates a
HandleConnection
object to which it passes a reference to the DateClient
's
socket details. The HandleConnection
class then creates an instance of the
DateTimeService
. It also establishes an input/output stream to the
DateClient
.
Next, as in Figure 10.8, “The Date Client passes the command to the HandleConnection.” the DateClient
passes a command
to the HandleConnection
. In this case the command is a String
object that contains the text "GetDate"
. When the HandleConnection
receives the String
object it compares it to see if it is a valid command. In our case
the only valid command is "GetDate" so if it is this command then the DateTimeService
is called to request the date/time. At this time, since we have not threaded our server application the
DateServer
is not actually listening to port 5050, rather it is waiting for a response
from the HandleConnection
object.
in Figure 10.9, “The HandleConnection gets the Date/Time and sends it to the Client.” the DateTimeService
sends the data/time details
back to the HandleConnection
object where it is then passed on to the
DateClient
as a String
object such as "Mon 15th..."
.
The DateClient
will then display the returned data on the client's machine.
Finally as in Figure 10.10, “The HandleConnection shuts down. The DateServer starts listening again.” the HandleConnection
object shuts
down connection and is destroyed. Control is returned to the DateServer
class and the server once
again begins listening to port 5050. The client is now disconnected after having received the data. It now
shuts down in our case. The next client is now free to connect to port 5050 on the DateServer
and everything happens again. The DateServer
will listen forever unless we shut it down
by typing CTRL-C in the terminal window.
Here you can see the client and server applications working as if you executed them from the command
prompt. The DateServer
is executed by typing java DateServer. There
is no need to specify a port number as 5050 is hard coded and there is no need to specify a host name as the
code will use the current machine as the server. Figure 10.11, “The DateServer running on "localhost"” shows the
DateServer
being executed and it stating that it has "started listening on port 5050".
The server then accepts 3 different connections from three clients that request the time. You will notice
that there is 10 seconds between each call.
Figure 10.12, “The Date Client specifing a server at "localhost"” displays the DateClient
running
four times. The first time that the client was run the server name was not specified, so the error was
displayed. The next time the client was executed correctly using java DateClient localhost
where the server is specified as being on the same machine (we could also have typed 127.0.0.1 instead of
localhost). The client connects to the server and received that the date is
Wed Jul 16 15:13:18 BST 2003
. The client disconnected and shut down. The client was
executed two further times at 10 second intervals.
There are four classes required for this example, as listed below. Download these classes into the one
directory - e.g. c:\temp
on your machine and compile them by typing
javac *.java. Once they have compiled successfully then open a second DOS/Unix command
prompt/terminal and execute the server in one by typing java DateServer and execute the
client in the second command prompt/server by typing java DateClient localhost where
both applications are on the one machine. If you want to run the client on another PC simply execute it
using java DateClient the.server.name where the.server.name is the IP/DNS name of the
machine on which the server is running.
Here is the source code for the DateTimeService
class that provides the date and
time to the HandleConnection
class - as below and as in
DateTimeService.java
1 2 3 4 // The DateTimeService class that provides the current date 5 // by Derek Molloy. 6 7 import java.util.*; 8 9 public class DateTimeService 10 { 11 private Date theDate; 12 13 //constructor gets the current date/time 14 15 public DateTimeService() 16 { 17 this.theDate = new Date(); 18 } 19 20 //method returns date/time as a formatted string 21 22 public String getDateAndTime() 23 { 24 return "The date is:" + this.theDate.toString(); 25 } 26 } 27 28
Here is the source code for the DateServer
class that runs the server application
and creates an instance of the HandleConnection
whenever a connection is made by
a client - as below and as in
DateServer.java
1 2 3 4 // The DateServer - The server application that passes control 5 // to the Handle Connection - by Derek Molloy 6 7 import java.net.*; 8 import java.io.*; 9 10 public class DateServer 11 { 12 public static void main(String args[]) 13 { 14 ServerSocket serverSocket = null; 15 try 16 { 17 serverSocket = new ServerSocket(5050); 18 System.out.println("Server has started listening on port 5050"); 19 } 20 catch (IOException e) 21 { 22 System.out.println("Error: Cannot listen on port 5050: " + e); 23 System.exit(1); 24 } 25 while (true) // infinite loop - loops once for each client 26 { 27 Socket clientSocket = null; 28 try 29 { 30 //waits here (forever) until a client connects 31 clientSocket = serverSocket.accept(); 32 System.out.println("Server has just accepted socket 33 connection from a client"); 34 } 35 catch (IOException e) 36 { 37 System.out.println("Accept failed: 5050 " + e); 38 break; 39 } 40 41 // Create the Handle Connection object - only create it 42 HandleConnection con = new HandleConnection(clientSocket); 43 44 if (con == null) //If it failed send and error message 45 { 46 try 47 { 48 ObjectOutputStream os = new ObjectOutputStream( 49 clientSocket.getOutputStream()); 50 os.writeObject("error: Cannot open socket thread"); 51 os.flush(); 52 os.close(); 53 } 54 catch (Exception ex) //failed to even send an error message 55 { 56 System.out.println("Cannot send error back 57 to client: 5050 " + ex); 58 } 59 } 60 else { con.init(); } // otherwise we have not failed to 61 // create the HandleConnection object 62 // run it now. 63 } 64 try // do not get here at the moment 65 { 66 System.out.println("Closing server socket."); 67 serverSocket.close(); 68 } 69 catch (IOException e) 70 { 71 System.err.println("Could not close 72 server socket. " + e.getMessage()); 73 } 74 } 75 } 76 77
Here is the source code for the HandleConnection
class that is created by
the DateServer
class whenever a connection is received. This class is responsible
for dealing with each of the client requests.
HandleConnection.java
1 2 3 // The Handle connection class - that deals will all client requests 4 // directly - by Derek Molloy 5 6 import java.net.*; 7 import java.io.*; 8 import java.util.*; 9 10 11 public class HandleConnection 12 { 13 14 private Socket clientSocket; // Client socket object 15 private ObjectInputStream is; // Input stream 16 private ObjectOutputStream os; // Output stream 17 private DateTimeService theDateService; 18 19 // The constructor for the connecton handler 20 21 public HandleConnection(Socket clientSocket) 22 { 23 this.clientSocket = clientSocket; 24 25 //Set up a service object to get the current date and time 26 theDateService = new DateTimeService(); 27 } 28 29 // The main execution method 30 public void init() 31 { 32 String inputLine; 33 34 try 35 { 36 this.is = new ObjectInputStream(clientSocket.getInputStream()); 37 this.os = new ObjectOutputStream(clientSocket.getOutputStream()); 38 while (this.readCommand()) {} 39 } 40 catch (IOException e) 41 { 42 e.printStackTrace(); 43 } 44 } 45 46 // Receive and process incoming command from client socket 47 48 private boolean readCommand() 49 { 50 String s = null; 51 52 try 53 { 54 s = (String)is.readObject(); 55 } 56 catch (Exception e) 57 { 58 s = null; 59 } 60 if (s == null) 61 { 62 this.closeSocket(); 63 return false; 64 } 65 66 // invoke the appropriate function based on the command 67 if (s.equals("GetDate")) 68 { 69 this.getDate(); 70 } 71 else 72 { 73 this.sendError("Invalid command: " + s); 74 } 75 return true; 76 } 77 78 private void getDate() // uses the date service to get the date 79 { 80 String currentDateTime = theDateService.getDateAndTime(); 81 this.send(currentDateTime); 82 } 83 84 85 // Send a message back through to the client socket as an Object 86 private void send(Object o) 87 { 88 try 89 { 90 System.out.println("Sending " + o); 91 this.os.writeObject(o); 92 this.os.flush(); 93 } 94 catch (Exception ex) 95 { 96 ex.printStackTrace(); 97 } 98 } 99 100 // Send a pre-formatted error message to the client 101 public void sendError(String msg) 102 { 103 this.send("error:" + msg); //remember a string IS-A object! 104 } 105 106 // Close the client socket 107 public void closeSocket() //close the socket connection 108 { 109 try 110 { 111 this.os.close(); 112 this.is.close(); 113 this.clientSocket.close(); 114 } 115 catch (Exception ex) 116 { 117 System.err.println(ex.toString()); 118 } 119 } 120 } 121 122 123
Here is the source code for the DateClient
class that is the only class
required on the client side. It connects to the server and requests the date/time.
DateClient.java
1 2 3 4 // The Date Client - The date client connects to the server, sends 5 // the "GetDate" command and waits for a response - by Derek Molloy 6 7 import java.net.*; 8 import java.io.*; 9 import java.util.*; 10 11 public class DateClient 12 { 13 14 private Socket socket = null; 15 private ObjectOutputStream os = null; 16 private ObjectInputStream is = null; 17 18 // the constructor expects the IP address of the server - the 19 // port is fixed at 5050 20 public DateClient(String serverIP) 21 { 22 if (!connectToServer(serverIP)) 23 { 24 System.out.println("Cannot open socket connection..."); 25 } 26 } 27 28 private boolean connectToServer(String serverIP) 29 { 30 try // open a new socket to port: 5050 and create streams 31 { 32 this.socket = new Socket(serverIP,5050); 33 this.os = new ObjectOutputStream(this.socket.getOutputStream()); 34 this.is = new ObjectInputStream(this.socket.getInputStream()); 35 System.out.print("Connected to Server\n"); 36 } 37 catch (Exception ex) 38 { 39 System.out.print("Failed to Connect to Server\n" + ex.toString()); 40 System.out.println(ex.toString()); 41 return false; 42 } 43 return true; 44 } 45 46 private void getDate() 47 { 48 String theDate; 49 System.out.println("GetDate"); 50 this.send("GetDate"); 51 theDate = (String)receive(); 52 if (theDate != null) 53 { 54 System.out.println("The Server's date is " + theDate); 55 } 56 } 57 58 // method to send a generic object. 59 private void send(Object o) 60 { 61 try 62 { 63 System.out.println("Sending " + o); 64 os.writeObject(o); 65 os.flush(); 66 } 67 catch (Exception ex) 68 { 69 System.out.println(ex.toString()); 70 } 71 } 72 73 // method to receive a generic object. 74 private Object receive() 75 { 76 Object o = null; 77 try 78 { 79 o = is.readObject(); 80 } 81 catch (Exception ex) 82 { 83 System.out.println(ex.toString()); 84 } 85 return o; 86 } 87 88 public static void main(String args[]) 89 { 90 if(args.length>0) 91 { 92 DateClient theApp = new DateClient(args[0]); 93 try 94 { 95 theApp.getDate(); 96 } 97 catch (Exception ex) 98 { 99 System.out.println(ex.toString()); 100 } 101 } 102 else 103 { 104 System.out.println("Error: you must provide the IP of the server"); 105 System.exit(1); 106 } 107 System.exit(0); 108 } 109 } 110 111 112
© 2006
Dr. Derek Molloy
(DCU).