How can I read from a BufferedReader in Java without blocking?
Asked Answered
T

4

3

I want to send a command to a server, and find out if I get a response.

Right now i am using BufferedReader's readline() function, which blocks until there's a response from server, but all I want to do is verify that there's a response from the server in the first place.

I tried using ready() or reset() to avoid this block, but it doesn't help.

This is causing my program to get stuck waiting for the server to respond, which never happens. InputStreamReader seems to do the same thing, by my understanding of things.

Other questions I found here on the subject didn't answer my question, so please if you can answer my question it will be great.

Trimetallic answered 30/6, 2012 at 11:41 Comment(2)
Interesting would be if the server did a flush() and ensure that a newline (println) was sent.Arboreous
What's the difference between blocking until there's a response and finding out whether there's a response? You have to wait on the server in either case.Tilly
G
1

May be all you need is the InputStream without wrapping it in a BufferedReader

while (inputStream.available() > 0) {
     int i = inputStream.read(tmp, 0, 1024);
     if (i < 0)
          break;
     strBuff.append(new String(tmp, 0, i));
}

I hope this helps.

Glib answered 30/6, 2012 at 11:48 Comment(9)
Changing to an InputStream is not required for this technique.Tilly
this does nothing to remove the blocking behaviorApart
@EJP Won't it give him the ability to stop waiting if there is no input ? In his question he said " but all I want to do is verify that there's a response from the server in the first place."Glib
You haven't understood what I wrote. BufferedInputStream would work just the same with this code.Tilly
@JarrodRoberson you are mistaken. If available() returns > 0 there is data that can be read without blocking, and read() will read that and not block.Tilly
this seems to work.. :) I didn't understand people's problem with this method, this doesn't block like BufferedInputStream.. thank you very much, if anybody else can explain to me again why this shouldn't be done I will be happy to hear, and thanks again for the answersTrimetallic
@Trimetallic Tge problem with using available() is that people think it will magically tell them when the incoming message is complete. It doesn't. The problem with this answer is that the first sentence is untrue.Tilly
I didn't want to know if the message is complete.. I wanted to know if I get any answer from server at all..because I have a device connected to IP Com, now the IP com is available, but if the device is disconnected then using read function will cause a block, can you demonstrate when using available could be a problem?Trimetallic
This doesn't help. The method "available" returns values which cannot be trusted. The java documentation warns about it. It may not be used for trying to avoid blocking read. Indeed, I have devices where "available" is always zero or always (!) non-zero .Unyielding
S
3

If you want to read responses asynchronously, I suggest starting a thread which read a BufferedReader. This is much simpler to code and easier to control.

Sodium answered 30/6, 2012 at 12:0 Comment(1)
I thought that BufferedReader is the best practice for reading data without blocking. But for some devices, "readLine" and even "ready" do block ("ready" shall never block): #27427713Unyielding
G
1

May be all you need is the InputStream without wrapping it in a BufferedReader

while (inputStream.available() > 0) {
     int i = inputStream.read(tmp, 0, 1024);
     if (i < 0)
          break;
     strBuff.append(new String(tmp, 0, i));
}

I hope this helps.

Glib answered 30/6, 2012 at 11:48 Comment(9)
Changing to an InputStream is not required for this technique.Tilly
this does nothing to remove the blocking behaviorApart
@EJP Won't it give him the ability to stop waiting if there is no input ? In his question he said " but all I want to do is verify that there's a response from the server in the first place."Glib
You haven't understood what I wrote. BufferedInputStream would work just the same with this code.Tilly
@JarrodRoberson you are mistaken. If available() returns > 0 there is data that can be read without blocking, and read() will read that and not block.Tilly
this seems to work.. :) I didn't understand people's problem with this method, this doesn't block like BufferedInputStream.. thank you very much, if anybody else can explain to me again why this shouldn't be done I will be happy to hear, and thanks again for the answersTrimetallic
@Trimetallic Tge problem with using available() is that people think it will magically tell them when the incoming message is complete. It doesn't. The problem with this answer is that the first sentence is untrue.Tilly
I didn't want to know if the message is complete.. I wanted to know if I get any answer from server at all..because I have a device connected to IP Com, now the IP com is available, but if the device is disconnected then using read function will cause a block, can you demonstrate when using available could be a problem?Trimetallic
This doesn't help. The method "available" returns values which cannot be trusted. The java documentation warns about it. It may not be used for trying to avoid blocking read. Indeed, I have devices where "available" is always zero or always (!) non-zero .Unyielding
P
1

It's a tricky task not get blocking if you use standard java IO. Common answer is migration to NIO or netty. Netty is more preferable choice. However sometimes you don't have a choice so I suggest you to try my workaround:

public String readResponse(InputStream inStreamFromServer, int timeout) throws Exception {
    BufferedReader reader = new BufferedReader(new InputStreamReader(inStreamFromServer, Charsets.UTF_8));
    char[] buffer = new char[8092];
    boolean timeoutNotExceeded;
    StringBuilder result = new StringBuilder();
    final long startTime = System.nanoTime();
    while ((timeoutNotExceeded = (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) < timeout))) {
        if (reader.ready()) {
            int charsRead = reader.read(buffer);
            if (charsRead == -1) {
                break;
            }
            result.append(buffer, 0, charsRead);
        } else {
            try {
                Thread.sleep(timeout / 200);
            } catch (InterruptedException ex) {
                LOG.error("InterruptedException ex=", ex);
            }
        }
    }
    if (!timeoutNotExceeded) throw new SocketTimeoutException("Command timeout limit was exceeded: " + timeout);

    return result.toString();
}

This workaround isn't a silver bullet but it has some important feature:

  • Doesn't use readline(). This method is dangerous for network communications because some servers don't return LF/CR symbols and your code will be stuck. When you read from a file it isn't critical you will reach end of the file anyway.
  • Doesn't use char symbol = (char) fr.read();. This approach is slower than reading to char[]
  • It has timeout functionality and you'll have possibility interrupt communication on slow connections
Pinchhit answered 12/4, 2020 at 16:25 Comment(2)
How was the sleeping time chosen? Could you switch Thread.sleep() for Thread.yield() instead?Pogey
according to documentation yield doesn't provide any guarantees. "A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint."Pinchhit
U
0

I did something similar recently using a CountDownLatch. There may be some better ways, but this is pretty easy, and seems to work reasonably well. You can adjust the wait tome of the CountDownLatch to suit your needs.

package com.whatever;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class TestRead {
    private static final Logger log = LoggerFactory.getLogger(TestRead.class);
    private CountDownLatch latch = new CountDownLatch(1);

    public void read() {
        URLReader urlReader = new URLReader();
        Thread listener = new Thread(urlReader);
        listener.setDaemon(true);
        listener.start();
        boolean success = false;
        try {
            success = latch.await(20000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error("error", e);
        }
        log.info("success: {}", success);
    }

    class URLReader implements Runnable {
        public void run() {
            log.info("run...");
            try {
                URL oracle = new URL("http://www.oracle.com/");
                URLConnection yc = oracle.openConnection();
                BufferedReader in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
                String inputLine;
                while ((inputLine = in.readLine()) != null)
                    System.out.println(inputLine);
                in.close();
                latch.countDown();
            } catch (Exception ex) {
                log.error("error", ex);
            }
            log.info("consumer is done");
        }

    }
    public static void main(String[] args) {
        TestRead testRead = new TestRead();
        testRead.read();
    }
}
Undermine answered 30/6, 2012 at 17:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.