简体   繁体   English

如何在不阻塞的情况下从 Java 中的 BufferedReader 读取?

[英]How can I read from a BufferedReader in Java without blocking?

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.现在我正在使用BufferedReaderreadline()函数,它会阻塞直到有来自服务器的响应,但我想要做的就是首先验证是否有来自服务器的响应。

I tried using ready() or reset() to avoid this block, but it doesn't help.我尝试使用ready()reset()来避免此块,但无济于事。

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. InputStreamReader似乎做同样的事情,根据我的理解。

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.我在这里找到的关于这个主题的其他问题没有回答我的问题,所以如果你能回答我的问题,那就太好了。

If you want to read responses asynchronously, I suggest starting a thread which read a BufferedReader.如果您想异步读取响应,我建议启动一个读取 BufferedReader 的线程。 This is much simpler to code and easier to control.这更易于编码且更易于控制。

May be all you need is the InputStream without wrapping it in a BufferedReader您可能只需要InputStream而不将其包装在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.我希望这有帮助。

I did something similar recently using a CountDownLatch.我最近使用 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.您可以调整 CountDownLatch 的等待时间以满足您的需要。

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

It's a tricky task not get blocking if you use standard java IO.如果您使用标准的 Java IO,那么不会阻塞是一项棘手的任务。 Common answer is migration to NIO or netty.常见的答案是迁移到 NIO 或 netty。 Netty is more preferable choice. Netty 是更可取的选择。 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().不使用 readline()。 This method is dangerous for network communications because some servers don't return LF/CR symbols and your code will be stuck.这种方法对于网络通信是危险的,因为有些服务器不返回LF/CR符号并且您的代码将被卡住。 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();不使用char symbol = (char) fr.read(); . . This approach is slower than reading to char[]这种方法比读取 char[] 慢
  • It has timeout functionality and you'll have possibility interrupt communication on slow connections它具有超时功能,您可能会在慢速连接上中断通信

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM