|
A Simple Server The following code implements a simple server in Java. The server allows an unlimited number of clients to connect to it. Each time a client sends the server a message, the server broadcasts the message to all of the clients (including the one that originally transmitted the message). Let's look at the code more carefully to see how it works! The program imports three libraries. The net.* libraries provide network functionality, the util.Vector library provides a vector (similar to an array) abstract data type, and the io.* libraries provide file io routines. EntryPoint is the name of the main class in the application. The main program begins by establishing a "ServerSocket" on port 45678. Once the socket is created, the main program enters an infinite loop. The server uses the "accept" method to wait for a client to connect to the server. When a client does connect, a new "Socket" is established connecting the server and client. A new "ClientHandler" thread (see below) is then constructed and launched. The server then waits for the next client to connect. If any exceptions arise, the application ends. Launching the server on a Unix box is quite simple. One must merely compile the "SimpleServer.java" code and then start the program via the "java SimpleServer.class" command. Now let's look at the "ClientHandler" class. The variable "clients" is a "static" Vector. That means that every single "ClientHandler" instance will share this variable. The socket that connects a particular client to the "EntryPoint" server is named "clientSocket". Each socket contains an input stream and an output stream. For a specific client, the "DataInputStream" (i.e. the stream from which a "ClientHandler" reads information from a client) is called "clientInput" and the "DataOutputStream" (i.e. the stream to which a "ClientHandler" writes information to send to a client) is called "clientOutput". The constructor for a "ClientHandler" sets the "Socket" to be the one that connects the client to the server. The specific "DataInputStream" and "DataOutputStream" associated with the "Socket" are determined and assigned. To set up these streams, a "getInputStream" or "getOutputStream" method must be called and then the result must be converted into a "BufferedInputStream" or "BufferedOutputStream" and then this result must be converted into a "DataInputStream" or "DataOutputStream". (Don't ask why, just do it!) Finally, the "Socket" is added to the "Vector" that is shared by all "ClientHandler"s. The "run" method for each "ClientHandler" works as follows. The current message ("msg") is set to null and then the "readUTF" method is invoked. The "read UTF" reads information for the "DataInputStream". If there is nothing to read, the "ClientHandler" thread sleeps until there is something. Once a message is received, the "ClientHandler" prints the message and then transmits the message to each client that is currently connected to the server, including the client that sent the message. Recall that the clients that are connected to the server all appear in the "Vector". To send a message to a client, the "ClientHandler" must invoke both the "writeUTF" and "flush" methods. The "writeUTF" method places information in the "DataOutputStream". The "flush" method transmits the information in the "DataOutputStream" to the client. Once the message is transmitted to all clients, the procedure repeats. Summary of Critical Classes and Methods The "ServerSocket" class has an "accept" method that returns a "Socket". The "Socket" class has methods called "getInputStream" and "getOutputStream". The "DataInputStream" class has a "readUTF" method. The "DataOutputStream" class has a "writeUTF", and "flush" method. // -------------------------------------- // SimpleServer.java // Mario Hewardt and John Paxton // August 23, 1998 // -------------------------------------- import java.net.*; // network stuff import java.util.Vector; // Vector stuff import java.io.*; // file io stuff class EntryPoint { public static void main (String args[]) { try { // ports 0 .. 2^16 ServerSocket newServer =new ServerSocket (45678); while ( true ) { Socket tmp = newServer.accept (); // wait for client ClientHandler newClient = new ClientHandler (tmp); newClient.start(); } } catch (Exception e ) { System.out.println("fell out of main"); System.exit(1); } } } // --------------------------------------- class ClientHandler extends Thread { ClientHandler ( Socket tmp ) { clientSocket=tmp; try { clientInput = new DataInputStream ( new BufferedInputStream (clientSocket.getInputStream())); clientOutput= new DataOutputStream (new BufferedOutputStream (clientSocket.getOutputStream())); } catch (Exception e ) { System.exit(1); } clients.addElement (this); } public void run ( ) { while ( true ) { String msg=null; try { msg = clientInput.readUTF (); } catch ( Exception e ) { System.out.println("read error"); System.exit(1); } System.out.println (msg + " " + clients.size()); // send the msg to the clients for ( int i=0; i<clients.size(); i++) { try { ClientHandler tmp=(ClientHandler)clients.elementAt (i); tmp.clientOutput.writeUTF (msg); tmp.clientOutput.flush(); } catch ( Exception e) {System.out.println("Client left");} } } } private static Vector clients = new Vector(); private Socket clientSocket; private DataInputStream clientInput; private DataOutputStream clientOutput; } |