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
]