Java Coffee Break Newsletter Volume 2, Issue 6 http://www.davidreilly.com/jcb/ ISSN 1442-3790 ================================================================ In this issue * Java in the news * Article : Handling network timeouts in Java ================================================================ In the News Here are a selection of recent news items that may be of interest to Java developers. /*/ Java to support Portable Network Graphics (PNG) Sun maintains a list of bugs, called the "Bug Parade". Recently, the bug/feature request for PNG support was closed. This makes it very likely that the next version of the Java 2 platform will support the PNG graphics format. /*/ Sun expands Java Certification Programme Sun is set to expand its current Java Certification Programme, in conjunction with other industry certifications from IBM, Novell, Oracle, and Netscape. The new scheme, called Jedi, will involve three levels, of which Sun Certified Java Programmer is the first. Details of the new Jedi certification programme are available from Sun Microsystem's Java site, at http://java.sun.com/cert-initiative/ /*/ Legal victories in Sun/Microsoft Java lawsuit Both parties have cause to celebrate, as well as to be concerned, after Judge Ronald Whyte issued three preliminary rulings in the lawsuit. While Microsoft appears to have violated their Java license, they are free to develop a cleanroom implementation. For further coverage, see CNET's spin, at http://www.news.com/News/Item/0,4,37017,00.html?st.ne.fd. gif.d /*/ Microsft assists developers of compiler and third party tools In a bid to encourage support for Microsoft J/Direct in third party Java development tools, such as compilers, IDEs, and JVMs, the "Developer Tools Interoperability Kit" has been released. This kit includes specification and compatibility tests for Microsoft J/Direct. The kit can be downloaded from http://www.microsoft.com/java/ ================================================================ Handling network timeouts in Java When writing network applications in a stable and controlled environment, it is easy to forget what the real world is like. Slow connections, traffic build ups, or power interruptions can cause network connections to stall or even die. Few programmers take the time to detect and handle network timeouts, but avoiding the problem can cause client applications to freeze, and for threads in servers to block indefinitely. There are, however, easy ways to handle network timeouts. In this article, I'll present two techniques for overcoming the problem of network timeouts: threads and setting a socket option for timeouts. -- David Reilly Overview Handling timeouts is one of those tasks that people generally leave to the last moment. In an ideal world, we'd never need them. In an intranet environment, where most programmers do their development, it's almost always unnecessary. However, on the Internet, good timeout handling is critical. Badly behaved clients may go offline and leaving server threads locked, or overloaded servers may stall, causing a network client to block indefinitely for input. For these reasons, it is necessary to detect and handle network timeouts. I've identified two relatively simple solutions to the problem of handling network timeouts. The first solution involves the use of second thread, which acts as a timer. This solution results in a slight increase in complexity, but is backwards compatible with JDK1.02. The second solution is by far, much simpler. It takes only a few lines of code, by setting a socket option, and catching a timeout exception. The catch (isn't there always one) is that it requires a JDK1.1 or higher virtual machine. Solution One : Using a timer thread Many network software (particularly servers), are written as multi-threaded applications. However, a client can also use multiple threads. One solution to handling network timeouts is to launch a secondary thread, the 'timer', and have it cancel the application gracefully if it becomes stalled. This prevents end users from becoming confused when the application stalls - a good error message will at least let them know the cause of the problem. Listing One shows a Timer, which can be used in networking applications to handle timeouts gracefully. Once the timer is started, it must be reset regularly, such as when data is returned by a server. However, if the thread that reads from a remote network host becomes stalled, the timer will exit with an error message. For those who require a custom handler, the timeout() method may be overridden to provide different functionality. Listing One - Timer.java /** * The Timer class allows a graceful exit when an application * is stalled due to a networking timeout. Once the timer is * set, it must be cleared via the reset() method, or the * timeout() method is called. *

* The timeout length is customizable, by changing the 'length' * property, or through the constructor. The length represents * the length of the timer in milliseconds. * * @author David Reilly */ public class Timer extends Thread { /** Rate at which timer is checked */ protected int m_rate = 100; /** Length of timeout */ private int m_length; /** Time elapsed */ private int m_elapsed; /** * Creates a timer of a specified length * @param length Length of time before timeout occurs */ public Timer ( int length ) { // Assign to member variable m_length = length; // Set time elapsed m_elapsed = 0; } /** Resets the timer back to zero */ public synchronized void reset() { m_elapsed = 0; } /** Performs timer specific code */ public void run() { // Keep looping for (;;) { // Put the timer to sleep try { Thread.sleep(m_rate); } catch (InterruptedException ioe) { continue; } // Use 'synchronized' to prevent conflicts synchronized ( this ) { // Increment time remaining m_elapsed += m_rate; // Check to see if the time has been exceeded if (m_elapsed > m_length) { // Trigger a timeout timeout(); } } } } // Override this to provide custom functionality public void timeout() { System.err.println ("Network timeout occurred.... terminating"); System.exit(1); } } To illustrate the use of the Timer class, here is a simple TCP client (Listing Two) and server (Listing Three). The client sends a text string across the network, and then reads a response. While reading, it would become blocked if the server stalled, or if the server took too long to accept the connection. For this reason, a timer is started before connecting, and then reset after each major operation. Starting the timer is relatively simple, but it must be reset after each blocking operation or the client will terminate. // Start timer Timer timer = new Timer(3000); timer.start(); // Perform some read operation ...... // Reset the timer timer.reset(); The server is relatively simple - it's a single-threaded application, which simulates a stalled server. The server reads a response from the client, and echoes it back.To demonstrate timeouts, the server will always "stall" for a period of twenty seconds, on every second connection. Remember though, in real life, server timeouts are entirely unpredictable, and will not always correct themselves after several seconds of delay. Listing Two import java.net.*; import java.io.*; /** * SimpleClient connects to TCP port 2000, writes a line * of text, and then reads the echoed text back from the server. * This client will detect network timeouts, and exit gracefully, * rather than stalling. * Start server, and the run by typing * * java SimpleClient */ public class SimpleClient { /** Connects to a server on port 2000, and handles network timeouts gracefully */ public static void main (String args[]) throws Exception { System.out.println ("Starting timer."); // Start timer Timer timer = new Timer(3000); timer.start(); // Connect to remote host Socket socket = new Socket ("localhost", 2000); System.out.println ("Connected to localhost:2000"); // Reset timer - timeout can occur on connect timer.reset(); // Create a print stream for writing PrintStream pout = new PrintStream ( socket.getOutputStream() ); // Create a data input stream for reading DataInputStream din = new DataInputStream( socket.getInputStream() ); // Print hello msg pout.println ("Hello world!"); // Reset timer - timeout is likely to occur during the read timer.reset(); // Print msg from server System.out.println (din.readLine()); // Shutdown timer timer.stop(); // Close connection socket.close(); } } Listing Three import java.net.*; import java.io.*; /** * SimpleServer binds to TCP port 2000, reads a line * of text, and then echoes it back to the user. To * demonstrate a network timeout, every second connection * will stall for twenty seconds. * * Start server by typing * * java SimpleServer */ public class SimpleServer extends Thread { /** Shall we stall? flag */ protected static boolean shallWeStall = false; /** Socket connection */ private Socket m_connection; /** * Constructor, accepting a socket connection * @param connection Connection to process */ public SimpleServer (Socket connection) { // Assign to member variable m_connection = connection; } /** Starts a simple server on port 2000 */ public static void main (String args[]) throws Exception { ServerSocket server = new ServerSocket (2000); for (;;) { // Accept an incoming connection Socket connection = server.accept(); // Process in another thread new SimpleServer(connection).start(); } } /** Performs connection handling */ public void run() { try { DataInputStream din = new DataInputStream ( m_connection.getInputStream() ); PrintStream pout = new PrintStream ( m_connection.getOutputStream() ); // Read line from client String data = din.readLine(); // Check to see if we should simulate a stalled server if (shallWeStall) { // Yes .. so reset flag and stall shallWeStall = false; try { Thread.sleep (20000); } catch (InterruptedException ie ) {} } else { // No.... but we will next time shallWeStall = true; } // Echo data back to clinet pout.println (data); // Close connection m_connection.close(); } catch (IOException ioe) { System.err.println ("I/O error"); } } } Solution Two : Simplified timeout handling with socket options A significantly easier solution to handling network timeouts is to set a socket option. Socket options allow programmers greater control over socket communication, and are supported by Java as of JDK1.1. One socket option in particular, SO_TIMEOUT, is extremely useful, because it allows programmers to specify an amount of time that a read operation will block for, before generating an java.io.InterruptedIOException, which can be easily caught to provide a timeout handler. We can specify a value for SO_TIMEOUT, by using the setSoTimeout() method. This method is supported by java.net.Socket, java.net.DatagramSocket, and java.net.ServerSocket. This is important, as it means we can handle timeouts in both the client and the server. The setSoTimeout accepts as its sole parameter an int, which represents the number of milliseconds an operation may block for. Settings this value to one thousand will result in timeouts after one second of inactivity, whereas setting SO_TIMEOUT to zero will allow the thread to block indefinitely. // Set SO_TIMEOUT for five seconds MyServerSocket.setSoTimeout(5000); Once the length for the timeout is set, any blocking operation will cause an InterruptedIOException to be thrown. For example, a DatagramSocket that failed to receive packets when the receive() method is called, would throw an exception. This makes it easy to separate our timeout handling code from the task of communicating via the network. The full article is available for free from the Java Coffee Break http://www.davidreilly.com/jcb/articles/network_timeouts/ ================================================================ The Java Coffee Break Newsletter is only sent out to email subscribers who have requested it, and to readers of the comp.lang.java.programmer and comp.lang.java.help newsgroups. If you'd like to receive our newsletter, and get the latest Java news, tips and articles from our site, then get your FREE subscription from http://www.davidreilly.com/jcb/newsletter/ If you are an email subscriber and no longer wish to receive the JCB Newsletter, please unsubscribe using the WWW form located at http://www.davidreilly.com/jcb/newsletter/unsubscribe.html