| By David Reilly | Article Rating: |
|
| May 1, 1998 12:00 AM EDT | Reads: |
14,574 |
When developing Java network applications in a stable and controlled environment, it's easy to become complacent and ignore the possibility of network timeouts. After all, with the perfect client and server running over a local area network, timeouts won't occur to stall your application. But when your users run clients or servers over the Internet (an environment where networks can go down, badly written software can stall or communication sessions can deviate from the ideal path of a communications protocol), timeouts can cause problems if there isn't a mechanism to recognize and deal with it.
In this article, I'll present two approaches to dealing with network timeouts. The first approach, writing a multi-threaded application, is backwards compatible with JDK1.02, at the expense of increased complexity. The second approach is by far the easiest. In most cases it involves adding only a few lines of code to your application but requires a JDK1.1 virtual machine. As an example, I'll show how the two approaches can be applied to a simple finger client.
Multi-threaded Applications
Designing a multi-threaded client or server offers a solution to the problem of timeouts. When one thread becomes blocked, waiting for network input or to send more data, a second timer thread can still perform other operations when a timeout occurs, such as terminating the connection. If you've never written a multi-threaded application before, consult the sidebar "Overview of Threads."
Listing 1 introduces the Timer class which is a subclass of java.lang.Thread. When an application creates an instance of Timer, it specifies the number of milliseconds that can elapse before a network timeout occurs. Once the timer is started, it will begin to decrement an internal counter. When the counter has no more milliseconds remaining, the timeout action occurs which, by default, is to exit the application. If necessary, subclasses of Timer can override the timeout method to provide custom functionality.
For the sample finger client, we will use the Timer class to terminate the application if a network timeout occurs (for example, a server that stalls). Listing 2 shows a multi-threaded finger client and Listing 3 shows a sample finger server. The client and server will communicate via port 79 (defined in the public int FINGER_PORT). However, on some systems the server may be unable to bind to this port, in which case an ephemeral port in the range 1024 to 5000 should be used instead for both client and server.
The finger server reads data from files matching the format 'user-name.info' where name is a valid username. For testing purposes, you can create a single user file and have the finger client request information. To simulate timeouts, the server maintains a counter and stalls after the first line of input on every second connection.
The finger client starts by checking its command line arguments and creating a new instance of itself. The client then opens a socket connection and gets input and output streams. It sends the name of a user to the finger server and then begins reading in data.
At some point in the connection, our finger server will stop sending data and our client will be stalled while it waits for input. Without some means of reconciliation, our client would remain blocked - this is where our Timer class solves the problem.
It is the responsibility of the application to repeatedly reset the timer to prevent a "false" timeout from occurring. While it is looping, the timer is reset; if the loop stops for some reason, the timer can continue uninterrupted and generate a timeout. When this occurs, the program can terminate rather than being locked.
If we wanted our timer to have different properties (for example, re-establishing a connection), we could simply extend the timer to create a custom timer class and to override the timeout method. Were we to write a server that supported timeouts, we would need a different timer thread for every single connection (which could cause considerable overheads if a large number of concurrent connections were generated). A much more efficient method is to use the new socket features introduced in JDK1.1, as we'll see in the next section.
Timeouts Made Easy
While the multi-threaded approach works well and is backwards compatible with JDK1.02, it does introduce a great deal of complexity, even for simple applications such as a finger client. If it is important to run your application on older virtual machines, use multi-threading. However, if you have the luxury of a JDK1.1 platform, you can add timeout code to your application with ease.
As part of the new features of JDK1.1, support for socket options have been introduced. One of these allows developers to specify the number of milliseconds that must elapse before a timeout occurs, throwing a java.io.InterruptedIOException. For Socket connections, this means that operations reading from the socket input stream can timeout if no data is received within a given period of time. With only a few lines of code, we can introduce support for timeouts, without moving to a multi-threaded design in our clients and without launching a Timer thread for our servers.
Simple Finger Client
Listing 4 shows a simple finger client which can be run against the server from Listing 3. You'll notice how simple it is to set a timeout value. The setSoTimeout method accepts an integer value as a parameter, representing the number of milliseconds for a timeout.
// Set SO_TIMEOUT for five seconds
socket.setSoTimeout ( 5000 );
All you need to do now is catch the java.io.InterruptedIOException that is thrown when a timeout occurs while reading from a Socket's input stream and your application will handle timeouts.
Timeouts in Servers
Timeouts can occur in servers as well. If you're writing a multi-threaded server, it's important to set and detect timeouts in the socket connections to clients. Otherwise, badly behaved clients can use up memory by stalling and leaving threads open, or use up all your available connections if your server has a limit to the number of connections it supports. A good design would be to call the setSoTimeout method on every socket you accept and to catch InterruptedIOExceptions if thrown.
The most important case for using the setSoTimeout method is when your server uses DatagramSockets. DatagramSockets allow Java applications and applets to communicate via UDP, which doesn't offer guaranteed packet delivery or ordering. It's critical that applications using UDP recognize and provide for situations in which timeouts occur. Previously, this meant creating a separate thread to monitor packet arrivals (and often re-sending packets when no response was issued). Now, however, your application can easily cater to this situation with a few lines of code using the setSoTimeout method.
To illustrate this, I've written an example program that implements a stop-and-wait delivery mechanism. One application (the sender) sends small packets of data containing a sequence number and then waits for an acknowledgement packet to be sent by the receiver. If no acknowledgement arrives, then one of two situations could have occurred:
1. The original packet never arrived at the receiver's end.
2. The original packet arrived and was acknowledged, but the acknowledgement never arrived at the sender.
Listing 5 shows the code for the sender and Listing 6 shows the code for the receiver. The sender uses a DatagramSocket to send packets, and receive acknowledgements. Likewise, the receiver uses a DatagramSocket to receive packets and send acknowledgements. Both sender and receiver should check for timeouts and respond accordingly.
Timeouts can occur for a variety of reasons; there may be times when the sender is forced to send more than one instance of a data packet, or when the receiver sends more than one acknowledgement. It is important in this situation for both sender and receiver to check the sequence number and handle inappropriate packets gracefully. It is even more important when your application sends many packets and expects the receiver to maintain sequence integrity.
Conclusion
The decision to use a timer thread, or to use socket options instead, is largely up to the individual circumstances of the application. Some applications will require backwards compatibility with JDK1.02, in which case timers may be the only option. Wherever possible though, due consideration should be given to setting socket options for timeouts.
Setting socket options for timeouts and catching InterruptedIOExceptions allows developers to place code close to the point at which input and output takes place. This reduces complexity and saves placing control of your connections in an external class. For clients that are not already multi-threaded, setting a socket option timeout is by far the better choice and with only a few lines your application can support network timeouts.
Published May 1, 1998 Reads 14,574
Copyright © 1998 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By David Reilly
David Reilly has worked on network protocols and Web-related programming at Bond University,
Australia. Since his conversion to Java in 1996, he has worked almost exclusively with the language, finding it both a joy to use and the most productive way to produce portable applications.
- Agile Adoption – Crossing the Chasm
- Cloud Expo New York: The Java EE 7 Platform - Developing for the Cloud
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- Cross-Platform Mobile Website Development – a Tool Comparison
- Architecture Governance – the TOGAF Way
- It's the Java vs. C++ Shootout Revisited!
- Twelve New Programming Languages: Is Cloud Responsible?
- Cloud Expo New York Speaker Profile: Arun Gupta – Oracle
- Agile Development & Enterprise Architecture Practice – Can They Coexist?
- Component Development and Assembly Using OSGi Services
- Cloud Expo New York: Industry-Leading CxOs to Present June 11-14
- Big Data: Information Spawns Innovation
- Agile Adoption – Crossing the Chasm
- Graal, a Dynamic Java Compiler in the Works
- Cloud Expo New York: The Java EE 7 Platform - Developing for the Cloud
- Write Once Run Anywhere or Cross Platform Mobile Development Tools
- Cross-Platform Mobile Website Development – a Tool Comparison
- Architecture Governance – the TOGAF Way
- Google Analytics with Monitis Dashboard
- It's the Java vs. C++ Shootout Revisited!
- Twelve New Programming Languages: Is Cloud Responsible?
- Cloud Expo New York Speaker Profile: Arun Gupta – Oracle
- Scaling Java and JSP Apps with Distributed Caching
- Agile Development & Enterprise Architecture Practice – Can They Coexist?
- A Cup of AJAX? Nay, Just Regular Java Please
- Java Developer's Journal Exclusive: 2006 "JDJ Editors' Choice" Awards
- JavaServer Faces (JSF) vs Struts
- The i-Technology Right Stuff
- Rich Internet Applications with Adobe Flex 2 and Java
- Java vs C++ "Shootout" Revisited
- Bean-Managed Persistence Using a Proxy List
- Reporting Made Easy with JasperReports and Hibernate
- Creating a Pet Store Application with JavaServer Faces, Spring, and Hibernate
- Why Do 'Cool Kids' Choose Ruby or PHP to Build Websites Instead of Java?
- What's New in Eclipse?
- i-Technology Predictions for 2007: Where's It All Headed?




















