How do you set a timeout on BufferedReader and PrintWriter in Java 1.4?
Asked Answered
H

4

17

How does one set a timeout on a BufferedReader and a PrintWriter created using a socket connection? Here is the code I have for the server right now, which works until either the server or the client crashes:

while(isReceiving){
    str = null;
    BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);

    while ((str = br.readLine()) != null){
        System.out.println("Processing command " + str);
        pw.println(client.message(str));
    }
}

Outside the scope of this code I have imposed a socket timeout of 1000ms, which works as intended when waiting for the initial connection. But the program blocks at (str = br.readLine()). If the client hangs or crashes, it never stops blocking unless I terminate the process (which even then doesn't always work).

The client code in question is very similar to this, and is blocking in a similar fashion.

Howardhowarth answered 22/7, 2011 at 16:10 Comment(0)
H
2

Since calling socket.close() did not seem to interrupt the block at br.readLine(), I did a little workaround. When disconnecting the client from the server, I merely send through a string "bye", and told the server to close the socket connection when it receives this command.

while ((str = br.readLine()) != null){
    // If we receive a command of "bye" the RemoteControl is instructing
    // the RemoteReceiver to close the connection.
    if (str.equalsIgnoreCase("bye")){
        socket.close();
            break;
    }
    System.out.println("Processing command " + str);
    pw.println(client.message(str));
}
Howardhowarth answered 22/7, 2011 at 19:31 Comment(3)
If you can send "bye" you can just close the socket, which will cause readLine() at the peer to return null. Hard to see how this solves the problem you stated.Hiram
It's been a while since I asked this question, but if memory serves the listening process continued to hang after closing the socket from the client. I remember that we were using some hokey customized implementation of Java which didn't always behave in the way one might expect.Howardhowarth
What you claim is not possible. Please retest it.Hiram
H
20
  1. You need to set a read timeout on the socket, with Socket.setSoTimeout(). This will cause any read method to throw a SocketTimeoutException if the read timeout specified expires. NB Read timeouts are set not on the stream but on the underlying Socket, via Socket.setSoTimeout().

  2. There is no such thing as a write timeout in TCP.

Hiram answered 23/7, 2011 at 8:48 Comment(3)
Above I posted my workaround for getting the desired behavior in my case, but there is no way to stop the block in this case? As noted in my question, I invoke socket.setSoTimeout(1000L) outside of what I did post, so shouldn't the socket time out at that point and end all read and write operations on the socket?Howardhowarth
@MetalSearGolid It should end read operations, with a SocketTimeoutException after the read has blocked for the timeout period. As i have already stated, there is no write timeout in TCP.Hiram
@YoussefDir That sets a read timeout, as I already stated in the answer. Not a write timeout,Hiram
H
17

You could use SimpleTimeLimiter from Google's Guava library.

Sample code (in Java 8):

BufferedReader br = ...;
TimeLimiter timeLimiter = new SimpleTimeLimiter();

try {
    String line = timeLimiter.callWithTimeout(br::readLine, 10, TimeUnit.SECONDS);
} catch (TimeoutException | UncheckedTimeoutException e) {
    // timed out
} catch (Exception e) {
    // something bad happened while reading the line
}
Hobson answered 24/7, 2015 at 13:20 Comment(8)
The posted link is dead, you can find the project now at github.com/google/guava/tree/master/guava/src/com/google/common/…Subbase
Updated my answer. Thanks!Hobson
...Java 8. The question said Java 1.4.Crosspiece
The principle described will work perfectly with Java 1.4 -- the only difference is the syntax.Hobson
The Guava code only works if the read() method concerned is interruptible. java.net.Socket reads are not.Hiram
@Hiram There is a method callUninterruptiblyWithTimeout. The source has a lot of switches to distinguish between interruptible and uninterruptible methods, so it seems to be aware and ready for other usecases.Scarecrow
@Scarecrow Did you read it? 'The difference with callWithTimeout() is that this method will Ignore interrupts on the current thread'. You can't just magically make an uninterruptible call interruptible, and this library doesn't do so, or claim to.Hiram
@MarquisofLorne I read it again; I think I had misinterpreted "callUninterruptibly" for "callUninterruptable". The "callWithTimeout" becomes uninterruptable, the callable argument still has to be. Sorry for the confusion.Scarecrow
W
5

An answer in this question describes an interesting method using a Timer to close the connection. I'm not 100% sure if this works in the middle of a read, but it's worth a shot.

Copied from that answer:

TimerTask ft = new TimerTask(){
   public void run(){
     if (!isFinished){
       socket.close();
     }
   }
};

(new Timer()).schedule(ft, timeout);

isFinished should be a boolean variable that should be set to true when you're done reading from the stream.

Whopping answered 22/7, 2011 at 16:16 Comment(5)
+1: I would use socket.close() which closes the socket and streams. You can close it more than once.Houser
You could also say what the isFinished test is for. ;)Houser
I'll give this a shot, but I'm skepitcal if socket.close() will work because I've already tried closing it that way... no dice. It shouldn't matter if the socket I'm trying to close is in another thread would it?Howardhowarth
This did not work, the br.readLine() still continues to block even after I close the socket. I did found a workaround though, I will post it as an answer shortly.Howardhowarth
@BendertheGreatest Not possible. You would have got a socket closed exception.Hiram
H
2

Since calling socket.close() did not seem to interrupt the block at br.readLine(), I did a little workaround. When disconnecting the client from the server, I merely send through a string "bye", and told the server to close the socket connection when it receives this command.

while ((str = br.readLine()) != null){
    // If we receive a command of "bye" the RemoteControl is instructing
    // the RemoteReceiver to close the connection.
    if (str.equalsIgnoreCase("bye")){
        socket.close();
            break;
    }
    System.out.println("Processing command " + str);
    pw.println(client.message(str));
}
Howardhowarth answered 22/7, 2011 at 19:31 Comment(3)
If you can send "bye" you can just close the socket, which will cause readLine() at the peer to return null. Hard to see how this solves the problem you stated.Hiram
It's been a while since I asked this question, but if memory serves the listening process continued to hang after closing the socket from the client. I remember that we were using some hokey customized implementation of Java which didn't always behave in the way one might expect.Howardhowarth
What you claim is not possible. Please retest it.Hiram

© 2022 - 2024 — McMap. All rights reserved.