Pages

timeouts for blocking I/O

Graphic shows the socket calls that are used in the nonblocking I/O and select() example program.
Many programmers dread the thought of handling network timeouts. A common fear is that a simple, single-threaded network client without timeout support will balloon into a complex multithreaded nightmare, with separate threads needed to detect network timeouts, and some form of notification process at work between the blocked thread and the main application. While this is one option for developers, it is not the only one. Dealing with network timeouts need not be a difficult task, and in many cases you can completely avoid writing code for additional threads.

When working with network connections, or any type of I/O device, there are two classifications of operations:
  • Blocking operations: Read or write stalls, operation waits until I/O device is ready
  • Nonblocking operations: Read or write attempt is made, operation aborts if I/O device is not ready
Java networking is, by default, a form of blocking I/O. Thus, when a Java networking application reads from a socket connection, it will generally wait indefinitely if there is no immediate response. If no data is available, the program will keep waiting, and no further work can be done. One solution, which solves the problem but introduces a little extra complexity, is to have a second thread perform the operation; this way, if the second thread becomes blocked the application can still respond to user commands, or even terminate the stalled thread if necessary.
This solution is often employed, but there is a much simpler alternative. Java also supports nonblocking network I/O, which can be activated on any Socket,ServerSocket, or DatagramSocket. It is possible to specify the maximum length of time that a read or write operation will stall before returning control back to the application. For network clients, this is the easiest solution and offers simpler, more manageable code.
The only drawback to nonblocking network I/O under Java is that it requires an existing socket. Thus, while this method is perfect for normal read or write operations, a connect operation can stall for a much longer period, since there is no method for specifying a timeout period for connect operations. Many applications require this ability; you can, however, easily avoid the extra work of writing additional code. I've written a small class that allows you to specify a timeout value for a connection. It uses a second thread, but the internal details are abstracted away. This approach works well, as it provides a nonblocking I/O interface, and the details of the second thread are hidden from view.

Nonblocking network I/O

The simplest way of doing something often turns out to be the best way. While it is sometimes necessary to use threads and blocking I/O, in the majority of cases nonblocking I/O lends itself to a far clearer and more elegant solution. With only a few lines of code, you can incorporate timeout supports for any socket application. Don't believe me? Read on.
When Java 1.1 was released, it included API changes to the java.net package that allowed programmers to specify socket options. These options give programmers greater control over socket communication. One option in particular, SO_TIMEOUT, is extremely useful, because it allows programmers to specify the amount of time that a read operation will block. We can specify a short delay, or none at all, and make our networking code nonblocking.
Let's take a look at how this works. A new method, setSoTimeout ( int ) has been added to the following socket classes:
  • java.net.Socket
  • java.net.DatagramSocket
  • java.net.ServerSocket
This method allows us to specify a maximum timeout length, in milliseconds, that the following network operations will block:
  • ServerSocket.accept()
  • SocketInputStream.read()
  • DatagramSocket.receive()
Whenever one of these methods is called, the clock starts ticking. If the operation is not blocked, it will reset and only restart once one of these methods is called again; as a result, no timeout can ever occur unless you perform a network I/O operation. The following example shows just how easy it can be to handle timeouts, without resorting to multiple threads of execution:
// Create a datagram socket on port 2000 to listen for incoming UDP packets DatagramSocket dgramSocket = new DatagramSocket ( 2000 );
// Disable blocking I/O operations, by specifying a five second timeout dgramSocket.setSoTimeout ( 5000 );
Assigning a timeout value prevents our network operations from blocking indefinitely. At this point, you're probably wondering what will happen when a network operation times out. Rather than returning an error code, which might not always be checked by developers, a java.io.InterruptedIOException is thrown. Exception handling is an excellent way of dealing with error conditions, and allows us to separate our normal code from our error-handling code. Besides, who religiously checks every return value for a null reference? By throwing an exception, developers are forced to provide a catch handler for timeouts.
The following code snippet shows how to handle a timeout operation when reading from a TCP socket:
// Set the socket timeout for ten seconds
connection.setSoTimeout (10000);
try
{
// Create a DataInputStream for reading from socket
DataInputStream din = new DataInputStream (connection.getInputStream());
// Read data until end of data
for (;;)
{
String line = din.readLine();
if (line != null)
System.out.println (line);
else
break;
}
}
// Exception thrown when network timeout occurs
catch (InterruptedIOException iioe)
{
System.err.println ("Remote host timed out during read operation");
}
// Exception thrown when general network I/O error occurs
catch (IOException ioe)
{
System.err.println ("Network I/O error - " + ioe);
}
With only a few extra lines of code for a try {} catch block, it's extremely easy to catch network timeouts. An application can then respond to the situation without stalling itself. For example, it could start by notifying the user, or by attempting to establish a new connection. When using datagram sockets, which send packets of information without guaranteeing delivery, an application could respond to a network timeout by resending a packet that had been lost in transit. Implementing this timeout support takes very little time and leads to a very clean solution. Indeed, the only time that nonblocking I/O is not the optimal solution is when you also need to detect timeouts on connect operations, or when your target environment does not support Java 1.1.

Timeout handling on connect operations

If your goal is to achieve complete timeout detection and handling, then you'll need to consider connect operations. When creating an instance ofjava.net.Socket, an attempt to establish a connection is made. If the host machine is active, but no service is running on the port that is specified in thejava.net.Socket constructor, a ConnectionException will be thrown and control will return to the application. However, if the machine is down, or if there is no route to that host, the socket connection will eventually time out on its own much later. In the meantime, your application remains frozen, and there is no way to change the timeout value.
Though the socket constructor call will eventually return, it introduces a significant delay. One way of dealing with this problem is to employ a second thread, which will perform the potentially blocking connect, and to continually poll that thread to see if a connection has been established.
This does not, however, always lead to an elegant solution. Yes, you could convert your network clients into multithreaded applications, but often the amount of extra work required to do this is prohibitive. It makes the code more complex, and when writing only a simple network application, the amount of effort required is difficult to justify. If you write a lot of network applications, you'd find yourself reinventing the wheel frequently. There is, however, a simpler solution.
I've written a simple, reusable class that you can use in your own applications. The class generates a TCP socket connection without stalling for long time periods. You simply call a getSocket method, specifying the hostname, port, and timeout delay, and receive a socket. The following example shows a connection request:
// Connect to a remote server by hostname, with a four second timeout
Socket connection = TimedSocket.getSocket("server.my-network.net", 23, 4000);
If all goes well, a socket will be returned, just like the standardjava.net.Socket constructors. If the connection cannot be established before your specified timeout occurs, the method will stop, and will throw anjava.io.InterruptedIOException, just as other socket-read operations would when a timeout has been specified using a setSoTimeout method. Pretty easy, huh?