Solution for
Programming Exercise 10.5


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 10.5: Write a client program for the server from Exercise 10.4. Design a user interface that will let the user do at least two things: Get a list of files that are available on the server and display the list on standard output. Get a copy of a specified file from the server and save it to a local file (on the computer where the client is running).


Discussion

One possible user interface would be to use a menu, as was done in the solution to Exercise 10.3. For variety, however, I decided on an interface based entirely on the command line. The first command line argument must be the name or IP address of the computer where a copy of the FileServer program from Exercise 10.4 is running. If that is the only command-line argument, then the client will contact the server and send an "index" command to the server. The server responds with a list of file names. The client reads these names and displays them on the screen. The program then ends. (If you want to give another command, you have to run the client program again with a new command line.)

If there are two command-line arguments, then the second argument must be the name of a file on the server. The client contacts the server and sends a "get" command. The server responds with a one-line message, either "error" or "ok". The client reads this message. If the message is "error", indicating that the requested file can't be sent, then the client just displays an error message to the user. If the message is "ok", then the server also sends the contents of the file. The client tries to create a local file of the same name. It reads the data from the server and saves it to that file. However, for safety, the client will not create a new file if a local file of the same name already exists. This is considered to be an error. (There was no particular reason to do things this way -- I just wanted to make my interface a bit fancier.)

Finally, I wanted to make it possible to save a downloaded file in a local file with a different name. For this, three command line arguments are used. The first is the server, the second is the name of the file on the server, and the third is the name of the local file where the downloaded file is to be saved. In this case, the program is willing to overwrite an existing file of the same name, so the command must be used with some care.

If the server program is running on the same computer as the client (for demonstration purposes), the the following command lines can be used to run the client:

      java FileClient  172.0.0.1
      java FileClient  172.0.0.1  datafile.txt
      java FileClient  172.0.0.1  datafile.txt  mycopy.dat
      java FileClient  172.0.0.1  datafile.txt  datafile.txt

The first command shows a list of files that are available on the server. The other three all try to get a file named "datafile.txt" from the server.

The actual programming of the client is fairly straightforward. The example program DateClient.java provides a model for opening a connection to the server and for sending and receiving data over the connection. You should be able to follow the solution, given below.

By the way, once you understand how the FileClient and FileServer examples work, it's not a big conceptual leap to understanding how the World Wide Web works. A Web server program is just a greatly souped-up version of the FileServer program. It has access to a collection of files. It receives and responds to requests for those files from client programs. To get a file, the client program -- a Web browser -- needs to know the computer on which the server is running and the name of the file. This information is encoded in the url address of a Web page -- just like it's given on the command line of the FileClient program. A Web page, of course, can contain links to other Web pages. The link includes a url with the necessary information for finding the page. To get the page, the Web browser just goes through the same process of contacting the specified server and requesting the specified file. (One big complication is that not all the files on a Web server are text files, so the client needs some way of knowing what type of data is stored in the file, and it needs to know how to handle data of that type.)


The Solution

   /*
       This program is a client for the FileServer server.  The 
       server has a list of available text files that can be
       downloaded by the client.  The client can also download
       the list of files.  When the connection is opened, the
       client sends one of two possible commands to the server:
       "index" or "get <file-name>".  The server replies to
       the first command by sending the list of available files.
       It responds to the second with a one-line message,
       either "ok" or "error".  If the message is "ok", it is
       followed by the contents of the file with the specified
       name.  The "error" message indicates that the specified
       file does not exist on the server.  (The server can also
       respond with the message "unknown command" if the command
       it reads is not one of the two possible legal commands.)
       
       The client program works with command-line arguments.
       The first argument must be the name or IP address of the
       computer where the server is running.  If that is the
       only argument on the command line, then the client
       gets the list of files from the server and displays
       it on standard output.  If there are two parameters,
       the second parameter is interpreted as the name of a
       file to be downloaded.  A copy of the file is saved
       as a local file of the same name, unless a file of
       the same name already exists.  If there are three
       arguments, the second is the name of the file to be
       downloaded and the third is the name under which the
       local copy of the file is to be saved.  This will
       work even if a file of the same name already exists.
       
       This program uses the non-standard class, TextReader.
   */
   
   import java.net.*;
   import java.io.*;
   
   public class FileClient {
   
      static final int LISTENING_PORT = 3210;
   
   
      public static void main(String[] args) {
      
         String computer;      // Name or IP address of server.
         Socket connection;    // A socket for communicating with
                               //                that computer.
         PrintWriter outgoing; // Stream for sending a command
                               //                to the server.
         TextReader incoming;  // Stream for reading data from
                               //                the connection.
         String command;       // Command to send to the server.
         
   
         /* Check that the number of command-line arguments is legal.
            If not, print a usage message and end. */
         
         if (args.length == 0 || args.length > 3) {
            System.out.println("Usage:  java FileClient <server>");
            System.out.println("    or  java FileClient <server> <file>");
            System.out.println(
                  "    or  java FileClient <server> <file> <local-file>");
            return;
         }
         
         /* Get the server name and the message to send to the server. */
         
         computer = args[0];
         
         if (args.length == 1)
            command = "index";
         else
            command = "get " + args[1];
         
         /* Make the connection and open streams for communication.
            Send the command to the server.  If something fails
            during this process, print an error message and end. */
         
         try {
            connection = new Socket( computer, LISTENING_PORT );
            incoming = new TextReader( connection.getInputStream() );
            outgoing = new PrintWriter( connection.getOutputStream() );
            outgoing.println(command);
            outgoing.flush();
         }
         catch (Exception e) {
            System.out.println(
                 "Can't make connection to server at \"" + args[0] + "\".");
            System.out.println("Error:  " + e);
            return;
         }
         
         /* Read and process the server's response to the command. */
         
         try {
            if (args.length == 1) {
                  // The command was "index".  Read and display lines
                  // from the server until the end-of-stream is reached.
               System.out.println("File list from server:");
               while (incoming.eof() == false) {
                  String line = incoming.getln();
                  System.out.println("   " + line);
               }
            }
            else {
                  // The command was "get <file-name>".  Read the server's
                  // response message.  If the message is "ok", get the file.
               String message = incoming.getln();
               if (! message.equals("ok")) {
                  System.out.println("File not found on server.");
                  return;
               }
               PrintWriter fileOut;  // For writing the received data to a file.
               if (args.length == 3) {
                     // Use the third parameter as a file name.
                   fileOut = new PrintWriter( new FileWriter(args[2]) );
               }
               else {
                     // Use the second parameter as a file name,
                     // but don't replace an existing file.
                   File file = new File(args[1]);
                   if (file.exists()) {
                      System.out.println("A file with that name already exists.");
                      System.out.println("To replace it, use the three-argument");
                      System.out.println("version of the command.");
                      return;
                   }
                   fileOut = new PrintWriter( new FileWriter(args[1]) );
               }
               while (incoming.peek() != '\0') {
                      // Copy lines from incoming to the file until
                      // the end of the incoming stream is encountered.
                   String line = incoming.getln();
                   fileOut.println(line);
               }
               if (fileOut.checkError()) {
                  System.out.println("Some error occurred while writing the file.");
                  System.out.println("Output file might be empty or incomplete.");
               }
            }
         }
         catch (Exception e) {
            System.out.println("Sorry, an error occurred while reading data from the server.");
            System.out.println("Error: " + e);
         }
         
      }  // end main()
      
   
   } //end class FileClient


[ Exercises | Chapter Index | Main Index ]