A Simple TCP Server

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.?;