Java HTTP Server - BufferedReaders & BufferedWriters

February 20, 2013

This past week I was tasked with writing an HTTP server in Java, in the process learning a ton about Sockets, ServerSockets, InputStreams, OutputStreams, BufferedWriters and BufferedReaders.

BufferedReaders and BufferedWriters will read or write using memory to hold information temporarily, avoiding unnecessary (and costly) system calls. Let’s look at some code that doesn’t use either of these buffered classes:

File newFile = new File("foobar.txt");
FileWriter fileWriter = new fileWriter(newFile);
for(int i  = 0; i < 20; i++) {
  fileWriter.write("Some text");
}
fileWriter.close();

So what is this code really doing? FileWriter does exactly what the name implies; writing some data to a file. The problem is that this code will make a system call every time we write(); something to the file. In this case, our code would make 20 system calls. Then imagine you had a file that had a thousand lines of text. You’d then have to make 1000 system calls. That’s not really very scalable, especially for an HTTP server which should be able to communicate with multiple clients simultaneously.

Let’s wrap our original FileWriter with a BufferedWriter:

File newFile = new File("foobar.txt");
FileWriter fileWriter = new FileWriter(newFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
for(int i  = 0; i < 20; i++) {
  bufferedWriter.write("Some text");
}
bufferedWriter.flush();
bufferedWriter.close();

Our BufferedWriter will now only write to the file when the buffer fills (or flush(); is called), thus increasing efficiency by eliminating unnecessary system calls. I’ve found the default buffer size is large enough for most purposes, but you can set a custom buffer size when you construct a new BufferdWriter/BufferedReader object by simply adding a second integer argument to your constructor:

File newFile = new File("foobar.txt");
FileWriter fileWriter = new FileWriter(newFile);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter, 9001);

And just like that, your buffer size is now over 9000.

BufferedReaders behave in the exact same way as BufferedWriters, just in reverse:

File existingFile = new File("foobar.txt");
FileReader fileReader = new FileReader(existingFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while( (line = bufferedReader.read()) != null ) {
  System.out.println(line);
}

Again, we’re avoiding unnecessary system calls by simply buffering our file reading.

The takeaway here is not to be afraid to use BufferedReaders and BufferedWriters. If anything, it will your program will run more efficiently and scale gracefully.