BufferedReader的readLine()方法会阻塞。

14

使用readLine()接收数据时,即使在消息末尾加上"\n"并使用.flush发送消息,读取消息的while循环仍然会阻塞。只有在关闭套接字连接时,它才会离开循环。

以下是客户端代码:

bos = new BufferedOutputStream(socket.
            getOutputStream());
bis = new BufferedInputStream(socket.
            getInputStream());
osw = new OutputStreamWriter(bos, "UTF-8");
osw.write(REG_CMD + "\n");
osw.flush();

isr = new InputStreamReader(bis, "UTF-8");
BufferedReader br = new BufferedReader(isr);

String response = "";
String line;

while((line = br.readLine()) != null){
   response += line;
}

以及服务器的代码:

BufferedInputStream is;
BufferedOutputStream os;

is = new BufferedInputStream(connection.getInputStream());
os = new BufferedOutputStream(connection.getOutputStream());

isr = new InputStreamReader(is);

String query= "";
String line;

while((line = br.readLine()) != null){
   query+= line;
}

String response = executeMyQuery(query);
osw = new OutputStreamWriter(os, "UTF-8");

osw.write(returnCode + "\n");
osw.flush();

我的代码在服务器上的while循环中被阻塞了。 谢谢。


将你的代码放在try/catch块中,并在finally块中关闭流/连接。 - happy
2
这似乎没有问题。你想知道为什么会出现这种行为吗?如何防止它发生? - Sinkingpoint
1
如果你只想要读取一行,为什么要使用 while 循环呢? - Achintya Jha
7个回答

23

BufferedReader将会持续读取输入内容,直到达到结束位置(文件、流或源等的结尾)。在这种情况下,“结束”就是套接字的关闭。只要Socket连接保持打开状态,您的循环就会运行,并且BufferedReader将仅等待更多的输入内容,在每次到达'\n'时进行循环。


嗯,我没想到那个结尾是关闭套接字。谢谢。现在我看看,我误读了文档! - Majid
对于非文件流的 eof,该文档相当模糊。 - Sinkingpoint
是 OP 的循环一直读取直到 EOS,而不是 BufferedReader。 - user207421
如果连接已经打开但缓冲区中没有任何行,它是否仍会循环? - the_prole
@the_prole - 循环将会继续... 但是它不会退出循环,而是br.readLine()调用将会阻塞直到有新的一行可用。 - Sinkingpoint

13

我尝试了很多解决方案,但唯一没有阻止执行的是以下内容:

BufferedReader inStream = new BufferedReader(new InputStreamReader(yourInputStream));
String line;
while(inStream.ready() && (line = inStream.readLine()) != null) {
    System.out.println(line);
}

inStream.ready()返回false,如果下一个readLine()调用将阻塞执行。


3
你救了我的一个月。我只需要反转 inStream.ready()(line = inStream.readLine()) != null。我的 InputStream 是从 java.lang.Process 中获取的。 - Lennoard Silva
@LennoardSilva 是的,我的也是从 java.lang.Process 获取的,因此使用其他方法时你不会知道何时没有更多内容可读。 - vovahost

3

这是由于 while 循环中的条件:while((line = br.readLine()) != null)

每次迭代都会读取一行,并且当 readLine 返回 null 时退出循环。

只有在到达文件末尾(=套接字已关闭)时,readLine 才返回 null,并且如果读取了 '\n',则返回一个字符串。

如果你想在 readLine 上退出循环,可以省略整个 while 循环,只需执行:

line = br.readLine()


1
这是因为InputStream还没有准备好读取,所以会在in.readLine()上阻塞。 请尝试这样做:
boolean exitCondition= false;

while(!exitCondition){
    if(in.ready()){
        if((line=in.readLine())!=null){
            // do whatever you like with the line
        }
    }
}

当然,你必须控制exitCondition。

另一个选择是使用nio包,它允许异步(非阻塞)读取,但这取决于你的需求。


1
最好避免使用readline()方法。此方法对于网络通信来说是危险的,因为某些服务器不返回LF/CR符号,你的代码会被卡住。当你从文件中读取时,这并不重要,因为你最终会到达文件末尾并关闭流。
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();
}

它具有超时功能,如果通信时间过长,您可以中断通信。

你必须确保你正在使用的协议。大多数协议都会指定它们的行尾行为。 - NomadMaker

0
如果你想在不被强制关闭套接字的情况下获取其中的内容,只需使用ObjectInputStream和ObjectOutputStream即可。 示例:
ObjectInputStream ois;
ObjectOutputStream oos;

ois = new ObjectInputStream(connection.getInputStream());

String dataIn = ois.readUTF(); //or dataIn = (String)ois.readObject();

oos = new ObjectOutputStream(connection.getOutputStream());
oos.writeUtf("some message"); //or use oos.writeObject("some message");
oos.flush();

.....

0

当套接字没有关闭时,readline()和read()将被阻塞。因此,您应该关闭套接字:

Socket.shutdownInput();//after reader
Socket.shutdownOutput();//after wirite

而不是 Socket.close();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接