Socket,BufferedReader在readLine()处挂起

21

我有一台服务器,最初它执行以下操作:

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

稍后它将套接字传递给另一个名为 "foo" 的类。这个类等待特定于应用程序的消息。

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

我的客户端发送了以下这个序列:

  • "bar\n"
  • "how are u?\n"
  • "\n"
  • "传递给foo\n"
  • "\n"

问题在于有时候"foo"没有收到它的响应。它在readLine()中挂起。

服务器中readLine()使用的是预读取缓冲区吗?导致"foo"类被饿死的可能性有多大?

如果我在客户端加一个sleep,它可以工作。但这样做总会成功的几率是多少?

  • "bar\n"
  • "how are u?\n"
  • "\n"
  • sleep(1000);
  • "传递给foo\n"
  • "\n"

如何解决这个问题?感谢任何关于此事的帮助。


你也可以在循环内使用 BufferedReader 的 ready() 方法来检查数据是否准备好,然后再尝试读取它。 - eee
这个修复方案是如何解决问题的? - ashim
2
ready() - 表示此流是否准备好被读取。通常与read()一起使用,不与readLine()一起使用。例如:while(true) { if (br.ready()) { br.read(cb); cb.flip(); String msg = cb.toString(); if (msg == null) break; } } 中的cb是特定缓冲区大小的CharBuffer。这种技术将允许读取缓冲区内允许的多行。 - eee
更多关于它的内容... https://dev59.com/OW435IYBdhLWcg3wuiuz - eee
4个回答

30

eee的解决方案完美地解决了我的问题。我一直试图读取SMTP会话的输出,但程序一直卡在这里:

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

更改为:

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

我可以读懂所有内容。顺便说一下,br是一个BufferedReader对象。


这是对我有效的解决方案。readLine() 没有任何原因卡住了,而 .ready() 对我完美地起作用了。 - Wael
3
在使用ready()后使用readLine()时要注意,ready()告诉你有数据可以读取,但这并不意味着它是一整行。因此,readLine()仍然可能会阻塞。 - lrxw
1
在我的情况下,br.ready() 总是为 false,而第一个情况却有效。 - Davide

2

第一个 BufferedReader 中已经有数据了(已从套接字中读取,不再可用),因此将第一个示例中创建的 BufferedReader 传递给读取应用程序特定消息的类,而不是从套接字创建新的 BufferedReader


我无法做到那一点。服务器不在我的控制之下。Foo类在我的控制之下。为什么BufferedReader需要预读?我们不能控制吗?BufferedReader不应该预读(通过询问),但不清空套接字。请注意,第一个“\n”确保服务器停止读取。 - ashim
它确实如此,你无法改变;这是其read方法的契约: http://docs.oracle.com/javase/1.5.0/docs/api/java/io/BufferedReader.html#read(char[], int, int) - lxgr

2

我曾经也遇到过同样的问题,以下是我的解决方案:

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

1

答案可能有点晚,但这是2020年最简单和最新的答案,只需使用简单的方式使用输入流read()方法从套接字服务器或客户端接收数据。

当客户端断开连接或服务器关闭连接时,将抛出EOFException异常。

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

但是inputStream.available()有时会返回0,当发送方发送消息并在read()消息上挂起时... - saulyasar

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