When you run the Java virtual machine, you specify a public class
name on the command line. Java looks into this class for a static
method called main
, and starts execution by calling this
method. We will place all of the code for the server in
main
, and call the name of the containing class,
Server
. The class has a simple structure: it contains no
member variable and only a single static method.
import java.io.*; import java.net.*; public final class Server { public static void main(String argv[]) throws Exception { . . . } }
The import
command tells Java which package to search
when resolving external class references. In our program, we need to
include references to two of the Java core packages, because these
packages include classes that we use in this program. The package
java.io
contains the classes for the input and output
streams that we use in this program, and the package
java.net
contains the class files for networking
functionality.
The first order of business is to retrieve two parameters from the command line:
// Get the port number and message from the command line. int port = Integer.parseInt(argv[0]); String messageToClient = argv[1];
The port
is where the server will listen for a TCP
connection request, and messageToClient
is the text the
server will send to the connecting client. Notice that we need to
convert the string representation of the port number to an integer
representation, which we do by using the parseInt
method
of the Integer
class.
Next, we open a socket as follows:
// Open a socket.
ServerSocket listenSocket = new ServerSocket( ? );
The new
operator creates a new instance of the class
ServerSocket
and returns a reference to it, which is
stored in the variable listenSocket
. This is the socket
on which the server will listen for a TCP connection request. In this
code segment, you need to replace ?
with a missing detail. The argument to pass to the ServerSocket
constructor should be obvious, but if it is not, you can search the
documentation of the Java
API to determine what it must be.
After establishing the listen socket, we need to issue the command
to accept an incoming TCP connection request. This is done with the
accept()
method of the ServerSocket
class.
// Listen for and accept a TCP connection request. Socket connectionSocket = listenSocket.accept();
This is a blocking call, because the thread of execution will halt
until a remote host sends a connection request to this socket. When
such a client makes contact, a new socket connection is negotiated and
accept()
returns a reference to it, which we store in a
variable called connectionSocket
.
Up to this point, communication with the client has occurred in the transport layer. But now that a TCP connection has been established, we can begin communicating with the client on the application layer. We are now going to: (1) get a text string from the client, (2) send a text string to the client, and (3) close the socket. Both text strings must be terminated by a new line character.
Communication through the socket is done by writing to the socket's
OutputStream
and reading from the socket's
InputStream
. References to both of these streams are
obtained from the socket object by calling the appropriate methods:
// Get a reference to the socket's InputStream and OutputStream. InputStream is = connectionSocket.getInputStream(); OutputStream os = connectionSocket.getOutputStream();
Before we start to read from the input stream and write to the output stream, we attach a series of helpful filters to these streams. By doing so, we will be able to simplify the reading and writing operations.
// Setup input stream filters.
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// Setup output stream filters.
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new ? ;
Now we are ready for application-level communication with the client. According to our trivial protocol, the first action we take is to get from the input stream the message being sent by the client.
// Get and display message from client.
String messageFromClient = br.readLine();
System.out.println( ? );
The readLine()
method blocks until it receives a
complete line, that is, a string terminated by an end-of-line marker.
When readLine()
returns, we print out the message we have
received, and then send our message to the client by writing into the
output stream.
// Send message to client. bw.write(messageToClient + "\n"); bw.flush();
Notice that we flush()
the output stream. The reason
for this is that we added a filter to buffer writes into the stream,
so that only when the buffer is full are the bytes actually sent into
the output stream of the socket. If we didn't flush the stream at
this point, we would not be sure that the client had received the
message before the server closes the socket. This may work in some
cases, and fail in others -- but we will not go into the details here.
The final task is to terminate gracefully by first closing the streams and the sockets.
// Close the streams. bw.close(); br.?; // Close the sockets. connectionSocket.close(); listenSocket.?;