Contents
A Simple HTTP Server - Part 1
A Simple HTTP Server - Multi Threading - Part 2
A Simple HTTP Server - ThreadPool - Part 3
The Java standard API provides us with the Thread class, that allows for code to execute concurrently. There are two ways of creating a Thread, either by inheriting from the Thread class and overriding the run() method or by implementing the Runnable interface and implementing the run() method.
In Listing 1 below, we create a ClientHandler class that implements the Runnable interface and takes a client socket as an argument in the constructor. We then move the logic to handle the client connection into the run() method.
Listing 1
class ClientHandler implements Runnable{
private Socket client = null;
public ClientHandler(Socket client){
this.client = client;
}
public void run(){
try{
// Get A BufferedReader/BufferedWriter to handle reading and writing to the stream.
BufferedReader requestReader =
new BufferedReader(new InputStreamReader(this.client.getInputStream()));
BufferedWriter responseWriter =
new BufferedWriter(new OutputStreamWriter(this.client.getOutputStream()));
// Important, we need to read all the data sent from
// the client before we can send a response.
while (true){
String headerLine = requestReader.readLine();
if (headerLine.length() == 0){
break;
}
}
// How original is this?
responseWriter.write("Hello World\n");
responseWriter.flush();
// Closing the client connection will close, both the input and output streams.
this.client.close();
}catch(IOException e){
try{
if(this.client != null){
this.client.close();
}
}catch(IOException e2){
}
}
}
}
Now that we have a Runnable class, we can create an instance of it and pass it to a Thread as shown in Listing 2.
Listing 2
import java.net.*;
import java.io.*;
class HttpServer{
public static void main(String args[]){
try{
// Create a new server socket and listen on port 9000
try (ServerSocket server = new ServerSocket(9000)){
// Continue to listen for client connections
while (true){
// Accept a client connection. accept() is a blocking method.
Socket client = server.accept();
Thread thread = new Thread(new ClientHandler(client));
thread.start();
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
What you should realize from looking at the code in Listing 2, is that a new thread is being created to handle each client. This is an extremely inefficient design as threads are resource intensive. We can test this by running the ab tool as we did in part 1 and then looking at the CPU/memory usage using a program like top on Linux. If you ran the ab tool, you would have also noticed that the requests per second have been drastically reduced.
To improve the performance of the server we need to use a ThreadPool. Using a ThreadPool will allow us to manage the number of threads and reuse them for subsequent requests. In the next article, we'll take a look at implementing a very basic ThreadPool.
-
A Simple HTTP Server - ThreadPool - Part 3
In this article, we develop a simple HTTP server using a ThreadPool
15 January 2023 - 3631 views -
A Simple HTTP Server - Part 1
In this article, I discuss how to create a simple single-threaded HTTP server.
06 December 2022 - 4105 views