Interview Questions

Example : A Client Tester

Java Network Programming - Sockets for Servers


(Continued from previous question...)

Example : A Client Tester

Example is a program called ClientTester that runs on a port specified on thecommand-line, shows all data sent by the client, and allows you to send a response to the client by typing it on the command line. For example, you can use this program to see the commands that Netscape Navigator sends to a server.

NOTE: Clients are rarely as forgiving about unexpected server responses as servers are about unexpected client responses. If at all possible, try to run the clients that connect to this program on a Unix system or some other platform that is moderately crash-proof. Don't run them on a Mac or Windows 98, which are less stable.

This program uses two threads: one to handle input from the client and the other to send output from the server. Using two threads allows the program to handle input and output simultaneously: it can be sending a response to the client while receiving a request--or, more to the point, it can send data to the client while waiting for the client to respond. This is convenient because different clients and servers talk in unpredictable ways. With some protocols, the server talks first; with others, the client talks first. Sometimes the server sends a one-line response; often, the response is much larger. Sometimes the client and the server talk at each other simultaneously. Other times, one side of the connection waits for the other to finish before it responds. The program must be flexible enough to handle all these cases. Example shows the code.

    Example : A Client Tester

    import java.net.*;
    import java.io.*;
import com.macfaq.io.SafeBufferedReader;
     // 
     
     
public class ClientTester {
     
 public static void main(String[] args) {
     
        int port;
        
        try {
          port = Integer.parseInt(args[0]);
        }  
        catch (Exception e) {
          port = 0;
        }
        
        try {
 ServerSocket server = new ServerSocket
      (port, 1);
System.out.println
   ("Listening for connections on port " 
           + server.getLocalPort(  ));
     
          while (true) {
 Socket connection = server.accept(  );
            try {
System.out.println
("Connection established with "+ connection);
Thread input = new InputThread
    (connection.getInputStream(  ));
              input.start(  );
              Thread output 
 = new OutputThread
    (connection.getOutputStream(  ));
              output.start(  );
// wait for output and input to finish 
              try {
                input.join(  );
                output.join(  );
              }
      catch (InterruptedException e) {
              }
            }
            catch (IOException e) {
              System.err.println(e); 
            }
            finally {
              try {
 if (connection != 
     null) connection.close(  );
              }
              catch (IOException e) {}
            }
          }
        }
        catch (IOException e) {
          e.printStackTrace(  );
        }
      
      }
     
    }
     
class InputThread extends Thread {
      
      InputStream in;
      
 public InputThread(InputStream in) {
         this.in = in;
       }
     
       public void run(  )  {
       
         try {     
           while (true) {
             int i = in.read(  );
             if (i == -1) break;
             System.out.write(i);
           }
         }
         catch (SocketException e) {
  // output thread closed the socket
         }
         catch (IOException e) {
           System.err.println(e);
         }
         try {
           in.close(  );
         }
         catch (IOException e) { 
         } 
     
      }
     
    }
     
 lass OutputThread extends Thread {
      
      Writer out;
        
public OutputThread(OutputStream out) {
  this.out = new OutputStreamWriter(out);
      }
     
      public void run(  ) {
     
        String line;
        BufferedReader in 
= new SafeBufferedReader
(new InputStreamReader(System.in));
        try {
          while (true) {
            line = in.readLine(  );
       if (line.equals(".")) break;
            out.write(line +"\r\n");
            out.flush(  );
          }   
        }
        catch (IOException e) { 
        } 
        try {
          out.close(  );
        }
        catch (IOException e) { 
        } 
        
       }
     
    }

The client tester application is split into three classes: ClientTester, InputThread, and OutputThread. The ClientTester class reads the port from the command-line, opens a ServerSocket on that port, and listens for incoming connections. Only one connection is allowed at a time, because this program is designed for experimentation, and a slow human being has to provide all responses. Consequently, we set an unusually short queue length of 1. Further connections will be refused until the first one has been closed.

An infinite while loop waits for connections with the accept( ) method. When a connection is detected, its InputStream is used to construct a new InputThread, and its OutputStream is used to construct a new OutputThread. After starting these threads, we wait for them to finish by calling their join( ) methods.

The InputThread is contained almost entirely in the run( ) method. It has a single field, in, which is the InputStream from which data will be read. Data is read from in one byte at a time. Each byte read is written on System.out. The run( ) method ends when the end of stream is encountered or an IOException is thrown. The most likely exception here is a SocketException thrown because the corresponding OutputThread closed the connection.

The OutputThread reads input from the local user sitting at the terminal and sends that data to the client. Its constructor has a single argument, an output stream for sending data to the client. OutputThread reads input from the user on System.in, which is chained to an instance of the SafeBufferedReader class developed in Chapter 4, Java I/O. The OutputStream that was passed to the constructor is chained to an OutputStreamWriter for convenience. The run( ) method for OutputThread reads lines from the SafeBufferedReader, and copies them onto the OutputStreamWriter, which sends them to the client. A period typed on a line by itself signals the end of user input. When this occurs, run( ) exits the loop and out is closed. This has the effect of also closing the socket, so that a SocketException is thrown in the input thread, which also exits.

For example, here's the output when Netscape Communicator 4.6 for Windows connected to this server:

D:\JAVA\JNP2\examples\11>java ClientTester 80
Listening for connections on port 80
Connection established with
Socket[addr=localhost/127.0.0.1,port=1033,localport=80]
GET / HTTP 1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 [en] (WinNT; I)
Host: localhost
Accept: image/gif, image/x-xbitmap,
image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8

<html><body><h1>
Hello Client!</h2></body></html>
.

(Continued on next question...)

Other Interview Questions