21

I have a server which initially does this:-

BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
for (;;) {
  String cmdLine = br.readLine();
  if (cmdLine == null || cmdLine.length() == 0)
     break; 
  ...
}

later it passes the socket to another class "foo" This class wait for application specific messages.

 BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
 appCmd=br.readLine();

My client sends this sequence:

  • "bar\n"
  • "how are u?\n"
  • "\n"
  • "passing it to foo\n"
  • "\n"

The problem is that sometimes "foo" does not get its response. It hangs in the readLine().

What is the chance that readLine() in the server is buffering up the data using the read ahead and "foo" class is getting starved?

If I add a sleep in the client side, it works. But what is the chance that it will always work?

  • "bar\n"
  • "how are u?\n"
  • "\n"
  • sleep(1000);
  • "passing it to foo\n"
  • "\n"

How to fix the problem? Appreciate any help on this regard.

bluish
  • 26,356
  • 27
  • 122
  • 180
ashim
  • 231
  • 1
  • 2
  • 5
  • You can also check whether the data is ready by using BufferedReader's `ready()` before you try to read it..do this inside the loop – eee May 13 '11 at 06:42
  • How does this fix the problem? – ashim May 13 '11 at 06:57
  • 2
    `ready() - tell whether this stream is ready to be read`. Usually, I use this together with `read()`. I don't use it with `readLine()` e.g.: `while(true) { if (br.ready()) { br.read(cb); cb.flip(); String msg = cb.toString(); if (msg == null) break; } }` cb is a `CharBuffer` of a certain buffer size. This technique will allow reading a number of lines allowed in the buffer. – eee May 13 '11 at 07:43
  • more on it ... http://stackoverflow.com/questions/5244839/dose-bufferedreader-ready-method-ensure-that-readline-method-does-not-return – eee May 13 '11 at 07:53

4 Answers4

29

eee's solution works perfectly. I was trying to read output from an SMTP conversation but it would block on:

while ((response = br.readLine()) != null) {
    ...Do Stuff
}

Changing to:

while (br.ready()) {
    response = br.readLine();
    ...Do Stuff
}

I can read everything just fine. br is a BufferedReader object, BTW.

mojado
  • 421
  • 5
  • 6
  • This was the solution that worked for me. The readLine() was getting stuck for no reason and the .ready() worked for me perfectly – Wael Aug 19 '16 at 13:33
  • 3
    Be careful in general when using readLine() after ready(). Ready() tells you that there is data to read, but it does not mean that it is a whole line. So readLine() can still block. – lrxw Jul 30 '18 at 13:03
  • In my situation br.ready() is always false while the first situation works – Davide Feb 10 '20 at 18:48
2

I had the same problem and here is my solution:

try {
    StringBuilder response = new StringBuilder();
    response.append("SERVER -> CLIENT message:").append(CRLF);
    //Infinite loop
    while (true) {
        //Checks wheather the stream is ready
        if (in.ready()) {
            //Actually read line 
            lastLineFromServer = in.readLine();
            //If we have normal behavior at the end of stream
            if (lastLineFromServer != null) {
                response
                        .append(lastLineFromServer)
                        .append(CRLF);
            } else {
                return response.toString();
            }
        } else {//If stream is not ready
            //If number of tries is not exceeded
            if (numberOfTry < MAX_NUMBER_OF_TRIES) {
                numberOfTry++;
                //Wait for stream to become ready
                Thread.sleep(MAX_DELAY_BEFORE_NEXT_TRY);
            } else {//If number of tries is exeeded
                //Adds warning that things go weired
                response
                        .append("WARNING \r\n")
                        .append("Server sends responses not poroperly.\r\n")
                        .append("Response might be incomplete.")
                        .append(CRLF);
                return response.toString();
            }
        }
    }
} catch (Exception ex) {
    ex.printStackTrace();
    return "";
}
2

There is data already in the first BufferedReader (that has been read from the socket, and is no longer available from the socket), so pass the BufferedReader created in the first example to the class that reads the app specific messages, rather then creating a new BufferedReader from the socket.

bluish
  • 26,356
  • 27
  • 122
  • 180
MeBigFatGuy
  • 28,272
  • 7
  • 61
  • 66
  • I can not do that. The server is not under my control. The foo class in under my control. Why should BufferedReader read ahead? Can't we control that? Should not BufferedReader reads ahead(by say enquireing ) but does not empty the socket. Note that the first "\n" makes sure that the server breaks from reading. – ashim May 13 '11 at 06:49
  • It does, and you can't change that; that's the contract of its read method: http://docs.oracle.com/javase/1.5.0/docs/api/java/io/BufferedReader.html#read(char[], int, int) – lxgr Feb 20 '12 at 10:56
1

The answer might be late but this is the simplest and latest answer in 2020, just use the simple way to receive the data from the socket server or client using the input stream read() method.

EOFException will be thrown when the client is disconnected or the server closed the connection.

private String waitForData() throws IOException {
    String data = "";
    do {
        int c = inputStream.read();
        if (c > -1) data += (char) c;
        else throw new EOFException();
    } while (inputStream.available() > 0);
    return data;
}
Googlian
  • 6,077
  • 3
  • 38
  • 44
  • but inputStream.available() sometimes return 0 and when sender send message and hang on read() message.... – saulyasar Nov 08 '21 at 16:23