Lab 10: Networking Using Java

Copyright RIT 2006
$Id: writeup.xml,v 1.6 2009/02/16 14:05:56 vcss232 Exp $


Goal

Objectives

  1. To learn simple networking concepts.
  2. Write a simple web server.
  3. Understand some of the classes in the java.net package. In particular, you will use the InetAddress (http://www.cs.rit.edu/usr/local/jdk/docs/api/java/net/InetAddress.html) , ServerSocket (http://www.cs.rit.edu/usr/local/jdk/docs/api/java/net/ServerSocket.html) , and Socket (http://www.cs.rit.edu/usr/local/jdk/docs/api/java/net/Socket.html) , classes.
  4. Write a client program that accesses the server.
  5. Integrate what you've learned about file access, threads, and networking.


Team Setup

You are to work on this lab in teams of size 2. Students who do not abide by this rule will not get full points for in-class participation.

Overview

In this lab you will learn how to use Java to write a simple web server and a client to make use of the web server.

Pre - Lab Work

  1. Review your class notes on Java networking.

  2. Read through the Overview of Networking section in the Java tutorial (http://java.sun.com/docs/books/tutorial/networking/overview/index.html) You may also find the section titled "All About Sockets" (http://java.sun.com/docs/books/tutorial/networking/sockets/) useful as well.

In-Lab Activities

Activity #1 : A Web Server of your own!

In simplest form, a web server is simply a program that transfers files to clients upon request. You will write a simple multithreaded web server that will send the contents of any text file, one line at a time, to any client that understands your (simple) protocol. Remember that web servers use the TCP/IP protocol, so the Java classes that support TCP/IP (rather than UDP -- User Datagram Protocol) are the classes you will need to use.

You have three classes to write for this exercise. Believe it or not, your web server class is the simplest of the three. Your web server is to be called MyWebServer.java, and here is what it should do:

MyWebServer

  1. First, MyWebServer should create a ServerSocket on a port of the server computer. Let the JVM pick a port for you; that way you will avoid the possible problem of another person, who is using the same computer, choosing the same port number for his/her web server. Pass in any value for the port number when creating the ServerSocket Have MyWebServer display the port it is listening on immediately. You will need this information for your client program later, so that your client can connect to MyWebServer.
  2. Second, MyWebServer should call the accept() method of the ServerSocket. This will make MyWebServer receptive to requests to connect to it.
  3. Third, when a connection occurs (i.e., when the accept() method returns to MyWebServer), MyWebServer should create a new instance of your second class, this one called Servant, and pass the Socket object returned by the accept() method to the constructor of Servant. Servant will be a separate Thread (that is, the class Servant will extend Thread), so the new Servant must be started as well. Have MyWebServer report the details of the connection (use the toString() method of the Socket class) on Standard Output. This will allow you to monitor what is going on when you test your programs working together.

Steps 2 and 3 should occur in an endless loop. Whenever a client wants to connect, MyWebServer will create a new instance of Servant to handle the new client.

The next class for you to write is the Servant class. The Servant class should extend the Thread class, because a Servant will be created by MyWebServer every time a client connects to MyWebServer. Here is what your Servant class should do:

Servant

  1. In the constructor for Servant, have the Servant store the reference to the Socket that MyWebServer passes to it. While you're at it, use the Socket object to get the InetAddress of the computer the client is connecting from (use the getInetAddress() method of the Socket). You could also store the port number of the client by using the getPort() method of the Socket, and the host name of Client computer by using the getHostName() method of the InetAddress. These other pieces of information can be used later to label output, and may, therefore, help with debugging.
  2. In the run() method of the Servant, create a BufferedReader to read from the client, and a PrintWriter to write to the client. Remember to set autoFlush to true for your Printwriter. You can wrap these streams around the InputStream returned from the Socket getInputStream() method and the OutputStream returned from the Socket getOutputStream() method.
  3. Servant should then send a prompt to the client asking for the name of a file the client would like to see displayed. Use the PrintWriter to send that message, and then use the readLine() method of your BufferedReader to read the response.
  4. When Servant receives a file name, Servant should create a second BufferedReader, this one associated with the file the Client wants to see. Recall from File I/O that we will want to enclose file opening in its own try/catch block to handle the problem of the file not being accessible. If the file cannot be opened, Servant should display an appropriate message to the client, close the Socket it is using to connect to the client, and return from its run() method.
  5. Once the file is open, Servant should enter a loop reading from the file a line at a time, and using the PrintWriter to send the line to the client. When Servant reaches the end-of-file, Servant should display a message on Standard Output saying that the tranfer has been completed, and then close the file, close the Socket, and return from the run() method.

In summary, the life-cycle of a Servant is to store information about the connection when the servant is created, send a prompt to the client, read the file name from the client, open the requested file, read the file and send the contents to the client a line at a time, close the file, close the connection, and die. A Servant has a finite life, but a meaningful role to play. Amen.

The last class you must write is the client, which you will name Solicitor.java. Solicitor is the second simplest class to write. Solicitor will interact with MyWebServer to ask to see a text file, and will display what it gets from MyWebServer on its own Standard Output. Here is what Solicitor must do:

Solicitor

  1. To run Solicitor, the client must supply the host name and port number being used by MyWebServer:

    
    java Solicitor Server_name Server_port
    

    Using the Server_name, the Solicitor will get the InetAddress of MyWebServer. With the InetAddress and the Server_port, Solicitor will create a Socket object to connect to MyWebServer.
  2. In a manner similar to what the Servant did, Solicitor should create a BufferedReader to read from the MyWebServer (from Servant, actually), and a PrintWriter to write to MyWebServer (to the Servant, actually). Remember to set autoFlush to true for your Printwriter. You can wrap these streams around the InputStream returned from the Socket getInputStream() method and the OutputStream returned from the Socket getOutputStream() method.
  3. This web server protocol is very simple: the client expects to be prompted by the web server for the name of a file. So, the first thing the Solicitor must do is read from its BufferedReader and display the message it gets from MyWebServer (from Servant) on the Standard Output of Solicitor. Then Solicitor should use a Scanner object to read from its Standard Input the name of the file the user wants to see. You can use the nextLine() method of Scanner for this. Solicitor will then send the line it reads from the Scanner to MyWebServer (Servant) using the PrintWriter.
  4. Solicitor should then enter a loop, reading from MyWebServer (Servant) with its BufferedReader and displaying each line it gets on its own Standard Output. When the line the Solicitor reads is null, it means the transfer is finished, and Solicitor should then close the socket and terminate.

To test your work, have one team member start the server on one node of the CS network, and have the other team member start one or several clients on other node(s) of the network. Convince yourself that you have created a mechanism for sharing information among users located anywhere on the network.

That's it! You've written a multithreaded web server of your own! The web servers of the world work just this way; it's just that they employ a more complex protocol that supports a wider variety of transfers. You have achieved the essential construction; all the rest is details.

If you care to study the operation of your server as it supports several clients simultaneously, put a delay of 1 second in your Solicitor after it prints each line from MyWebServer. Using several Solicitors, you should be able to start several transfers of files of reasonable size, and you will see very clearly how independently your threads handle the work.

Although this is not required for the lab submission, you might want to enhance your prototcol in some ways. You could allow your Solicitor to cancel its request when a user types "exit" instead of a file name in response to the prompt for a file to display. In that case, your Servant class would have to look for that message and take appropriate action to close its Socket and return from its run() method.

You could also improve MyWebServer by creating a way to kill it (Good idea!!). As described, once MyWebServer starts, there is no graceful way to terminate it. One idea is to give the ServerSocket of MyWebServer a timeout. If no Solicitor tries to connect within the timeout period, you can make MyWebServer quit using a System.exit(0). Use the setSoTimeout() method of the ServerSocket to set the timeout to something like 2 minutes. When no client tries to connect within this time period, the ServerSocket will generate a SocketTimeoutException which the program can catch. When you catch the SocketTimeoutException, you can issue a message to Standard Output and exit the web server.

How To Submit

Once you are convinced that your programs are working correctly, submit your code using the following command:

try grd-232 lab10-1 MyWebServer.java Servant.java Solicitor.java


Grade Computation

Grade Breakdown: