How Java Web Servers Work
Pages: 1, 2, 3
The ServerSocket Class
The Socket class represents a "client" socket; a socket that
you construct whenever you want to connect to a remote server application. If
you want to implement a server application, such as an HTTP server or an FTP
server, you need a different approach. This is because your server must stand
by all the time, as it does not know when a client application will try to
connect to it.
For this purpose, you need to use the java.net.ServerSocket
class. This is an implementation of a server socket. A server socket waits for
a connection request from a client. Once it receives a connection request, it
creates a Socket instance to handle the communication with the
client.
To create a server socket, you need to use one of the four constructors the
ServerSocket class provides. You need to specify the IP address
and port number on which the server socket will listen. Typically, the IP
address will be 127.0.0.1, meaning that the server socket will be listening on
the local machine. The IP address the server socket is listening on is referred
to as the binding address. Another important property of a server socket is its
backlog, which is the maximum queue length for incoming connection requests
before the server socket starts to refuse incoming requests.
One of the constructors of the ServerSocket class has the
following signature:
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
For this constructor, the binding address must be an instance of
java.net.InetAddress. An easy way to construct an
InetAddress object is by calling its static method
getByName, passing a String containing the host name:
InetAddress.getByName("127.0.0.1");
The following line of code constructs a ServerSocket that
listens on port 8080 of the local machine with a backlog of 1.
new ServerSocket(8080, 1, InetAddress.getByName("127.0.0.1"));
Once you have a ServerSocket instance, you can tell it to wait
for incoming connection requests by calling the accept method.
This method will only return when there is a connection request. It returns an
instance of the Socket class. This Socket object can
then be used to send and receive byte streams from the client application, as
explained in the The Socket Class. Practically, the accept method is the only method used in the application
accompanying this article.
|
Source Code Download the HowWebServersWork.zip file for the example application. |
The Application
Our web server application is part of the ex01.pyrmont package
and consists of three classes:
HttpServerRequestResponse
The entry point of this application (the static main method) is
in the HttpServer class. It creates an instance of
HttpServer and calls its await method. As the name
implies, await waits for HTTP requests on a designated port,
processes them, and sends responses back to the clients. It keeps waiting until
a shutdown command is received. (The method name await is used
instead of wait because wait is an important method
in the System.Object class for working with threads.)
The application only sends static resources, such as HTML and image files, from a specified directory. It supports no headers (such as dates or cookies).
We'll now take a look at the three classes in the following subsections.
The HttpServer Class
The HttpServer class represents a web server and can serve
static resources found in the directory indicated by the public static final
WEB_ROOT and all subdirectories under it. WEB_ROOT is
initialized as follows:
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
The code listings include a directory called webroot that contains some static resources that you can use for testing this application. You can also find a servlet that will be used for my next article, "How Servlet Containers Work."
To request a static resource, type the following URL in your browser's Address or URL box:
http://machineName:port/staticResource
If you are sending a request from a different machine from the one running
your application, machineName is the name or IP address of the
computer running this application. If your browser is on the same machine, you
can use localhost for the machineName.
port is 8080 and staticResource is the name of the
file requested and must reside in WEB_ROOT.
For instance, if you are using the same computer to test the application and
you want to ask the HttpServer to send the index.html file,
use the following URL:
http://localhost:8080/index.html
To stop the server, send a shutdown command from a web browser by typing the
pre-defined string in the browser's Address or URL box, after the
host:port section of the URL. The shutdown command is defined by
the SHUTDOWN static final variable in the HttpServer
class:
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
Therefore, to stop the server, you can use:
http://localhost:8080/SHUTDOWN
Now, let's have a look at the await method that is given in Listing 1.1. The explanation of the code is to be found right after the listing.
Listing 1.1. The HttpServer class' await
method
public void await() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1,
InetAddress.getByName("127.0.0.1"));
}
catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
// Loop waiting for a request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
Request request = new Request(input);
request.parse();
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}
catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
The await method starts by creating a ServerSocket
instance and then going into a while loop.
serverSocket = new ServerSocket(
port, 1, InetAddress.getByName("127.0.0.1"));
...
// Loop waiting for a request
while (!shutdown) {
...
}
The code inside of the while loop stops at the accept method of
ServerSocket, which returns only when an HTTP request is received
on port 8080:
socket = serverSocket.accept();
Upon receiving a request, the await method obtains the
java.io.InputStream and the java.io.OutputStream
objects from the Socket instance returned by the
accept method.
input = socket.getInputStream();
output = socket.getOutputStream();
The await method then creates a Request object and
calls its parse method to parse the raw HTTP request.
// create Request object and parse
Request request = new Request(input);
request.parse();
Next, the await method creates a Response object,
sets the Request object to it, and calls its
sendStaticResource method.
// create Response object
Response response = new Response(output);
response.setRequest(request);
response.sendStaticResource();
Finally, the await method closes the Socket and
calls the getUri method of Request to check if the
URI of the HTTP request is a shutdown command. If it is, the shutdown variable
is set to true and the program exits the while loop.
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);