How do you set a timeout on BufferedReader and Pri

2019-01-26 05:52发布

问题:

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.

回答1:

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
}


回答2:

  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.



回答3:

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.



回答4:

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));
}