19.2 SERVER SOCKETS IN JAVA


19.2 SERVER SOCKETS IN JAVA

We will now talk about the server end of a client-server communication link. This we will do with the help of a bare-bones chat-room program consisting of a chat server class, ChatServer, and a multithreadable client handler class, ClientHandler. We will show how a ChatServer is able to provide chat-room services to multiple clients simultaneously.

A server program constructs a server socket by invoking the ServerSocket constructor, as in the following statement

     ServerSocket server = new ServerSocket( int portNumber ); 

Like the Socket class, the ServerSocket class is defined in the package java.net. The above invocation will cause a server program to monitor the port portNumber. For illustration, our ChatServer class shown below contains the following statement

      ServerSocket server = new ServerSocket( 5000 ); 

in line (B). This will cause the server program to monitor port 5000. Another way of interpreting this statement is that it registers port 5000 for the chat server and binds the server to that port. While the chat server is in service, no other server will be allowed to use this port on the same machine.

There is also a two-argument version of the ServerSocket constructor whose second argument specifies the maximum number of clients allowed to connect to the server at one time. For example, the invocation

     ServerSocket server = new ServerSocket( 5000, 10 ); 

says that no more than 10 clients will be allowed to connect with this server at any given time through port 5000.

Once a server is established by constructing a ServerSocket object in the manner shown above, the server program can be made to wait indefinitely for clients to connect by invoking

     Socket socket = server.accept(); 

When a client connects on the designated port, this statement returns a Socket object that can then be used to establish a two-way communication with the client through the I/O stream objects that can be constructed from the socket. Each such socket can be handed off to a separate thread of a multithreadable client handler.

We show below the ChatServer class. We set up a server in line (B). Then, inside a forever loop, we invoke the accept method on server to enable any number of clients to connect with this server. Each socket delivered by accept in line (C) is handed off in line (D) to a new thread of ClientHandler. In line (E), each new client is pushed into the back end of the ArrayList container declared in line (A). Some sort of a list of all the clients is needed to broadcast each chat participant's words to the rest of the clients.

 
class ChatServer { public static List clientList = new ArrayList(); //(A) public static void main( String[] args ) { try { ServerSocket server = new ServerSocket( 5000 ); //(B) for (;;) { //wait for client to connect Socket socket = server.accept(); //(C) System.out.print( "A new client checked in: " ); ClientHandler clh = new ClientHandler( socket ); //(D) clientList.add( clh ); //(E) clh.start(); //(F) } } catch(Exception e) { System.out.println(e); } } }

This brings us to the code for the ClientHandler class. This class has to serve the following purposes:

  1. Its constructor must first establish the I/O streams and then, for convenience, the Reader and Writer objects for reading information sent by a client and writing information to the client.

  2. It must print on a client's terminal a welcome message.

  3. It must ask a client for a name that the client would want to use in the chat room for identifying himself/herself. It must pre-pend all the messages from a client with his or her name.

  4. It must tell a client to type "bye" for signing off from the chat room.

  5. Upon receiving a "bye" from a client, it must close the I/O streams associated with that client and also terminate the thread. When a client signs off, this information must be broadcast to all other clients.

  6. When a client first signs in, it must print on the client's terminal all of the chat that has taken place up to that point.

  7. Finally, and most importantly, it must broadcast each client's messages to all other clients.

Task 1 above, that is establishing the I/O streams, and so on, for a client socket, named sock, can be accomplished by statements like

     PrintWriter out = new PrintWriter( sock.getOutputStream() );   //(G)     InputStream in_stream = sock.getInputStream();                 //(H)                                                                     InputStreamReader in_reader =                                  //(I)                 new InputStreamReader( in_stream );     BufferedReader buff_reader =                    new BufferedReader( in_reader );                //(J) 

In line (G), we invoke the getOutputStream method on a client socket to construct a PrintWriter output stream for sending information to the client. Lines (H), (I), and (J) construct a BufferedReader input stream for reading the information received from a client. With the PrintWriter object out, the Tasks 2, 3, and 4 listed above can be taken care of simply by statements like:

    out.println( "\n\nWelcome to the chat room");    out.println( "Type \"bye\"  in a new line to terminate session." );    out.print( "Please enter your first name: " );    String userName = buff_reader.readLine(); 

When a new client signs in, Task 6 can be handled by the following code fragment at the beginning of the interaction with the client:

     // chatStore is a static data member of type List for     // the class ClientHandler    if ( chatStore. size() != 0 ) {        out.println( "Chat history:\n\n" );        ListIterator iter = chatStore.listIterator();        while ( iter.hasNext() ) {            out.println( ( String ) iter.next() );        }        out.print("\n\n");        out.flush();    }  

Each message entered by a client is stored in the ArrayList object chatStore. When a new client first signs in, the while loop above prints out on the client's terminal the previously accumulated contents of chatStore.

With regard to Task 7, the following code takes care of displaying each client's message on the terminals of all other clients:

    ListIterator iter = ChatServer.clientList.listIterator();    while ( iter.hasNext() ) {                                   //(K)        ClientHandler cl = (ClientHandler) iter.next();        if ( this != cl ) {                                      //L)            cl.out.println();            cl.out.println( strWithName );                       //(M)            cl.out.print( cl.userName + ": " );                  //(N)            cl.out.flush();         }     } 

We go through all the clients stored in the list ChatServer.clientList one by one in the while loop in line (K). Since we do not want to send a message received from a client back to the same client, in line (L) we make sure that the current client is excluded from the loop. In line (M), we send the the string strWithName to all other clients; this string is merely the current client's message string pre-pended by his/her name. The purpose of line (N) is to make sure that new line on the other client's terminal screen has his/her name at the beginning.

Regarding Task 6, when a client sends "bye", this task can be handled by a statement block like

     buffered_reader.close();         out.close();     sock.close(); 

Shown below is all of the source code. On the server side, you compile the program, and then run the server by

     java ChatServer 

If a client does not have access to a GUI[3] for interacting with the chat server, he/she may still be able to participate in a chat through a simple telnet connection by typing the following string in a terminal window:

     telnet <internet name of your machine> 5000 

If, for the purpose of experimenting with the code, you wish to connect to the server through multiple windows on the same machine on which you are running the server, you can create clients in separate windows by entering

     telnet 127.0.0.1 5000 

in each window. The IP address 127.0.0.1, called the local loopback address, designates your local machine.

 
//ChatServer.java import java.io.*; import java.net.*; import java.util.*; public class ChatServer { public static List clientList = new ArrayList(); public static void main( String[] args ) { try { ServerSocket server = new ServerSocket( 5000 ); for (;;) { Socket socket = server.accept(); System.out.print( "A new client checked in: " ); ClientHandler clh = new ClientHandler( socket ); clientList.add( clh ); clh.start(); } } catch( Exception e ) { System.out.println( e ); } } } ///////////////// class ClientHandler extends Thread //////////////// class ClientHandler extends Thread { private String userName; private Socket sock; private static List chatStore = new ArrayList(); private BufferedReader buff_reader = null; private PrintWriter out = null; public ClientHandler( Socket s ) { try { sock = s; out = new PrintWriter( sock.getOutputStream() ); InputStream in_stream = sock.getInputStream(); InputStreamReader in_reader = new InputStreamReader( in_stream ); buff_reader = new BufferedReader( in_reader ); // ask for user name out.println( "\n\nWelcome to Avi Kak's chatroom"); out.println(); out.println( "Type \"bye\" in a new line to terminate session. \n" ); out.print( "Please enter your first name: " ); out.flush(); userName = buff_reader.readLine(); out.print("\n\n"); out.flush(); System.out.print( userName + "\n\n" ); // show to new client all the chat // that has taken place so far if ( chatStore.size() != 0 ) { out.println( "Chat history:\n\n" ); ListIterator iter = chatStore.listIterator(); while ( iter.hasNext() ) { out.println( (String) iter.next() ); } out.print("\n\n"); out.flush(); } } catch(Exception e) {} } public void run() { try { boolean done = false; while ( !done ) { out.print( userName + ": " ); out.flush(); String str = buff_reader.readLine(); if ( str.equals( "bye" ) ) { str = userName + " signed off"; done = true; } String strWithName = userName + ": " + str; chatStore.add( strWithName ); ListIterator iter = ChatServer.clientList.listIterator(); while ( iter.hasNext() ) { ClientHandler cl = (ClientHandler) iter.next(); if ( this != cl ) { cl.out.println(); cl.out.println( strWithName ); cl.out.print( cl.userName + ": " ); cl.out.flush(); } } } System.out.println( userName + " signed off" + "\n\n" ); buff_reader.close(); out.close(); sock.close(); } catch (Exception e) {} } }

[3]A homework problem at the end of this chapter is about designing an applet that a client can download for a more pleasing interaction with the chat server shown here.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net