BufferedReader永远不准备好(Java中的Socket编程)

7

我已经声明了一个socket,就像这样:

serverAddr = InetAddress.getByName(this.ip);
socket = new Socket(serverAddr, port);
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

然而,以下代码无法正常工作。 in.ready() 总是返回 false,如果删除此行代码,则程序将在 String message = in.readLine(); 处卡死。
private void receive() {
        try {
            InputStreamReader isr = new InputStreamReader(socket.getInputStream());
            System.out.println(isr.getEncoding());
            BufferedReader in = new BufferedReader(isr);
            if (in.ready()) {
                String message = in.readLine();
                if (message != null) {
                    if (listener != null) {
                        listener.receiveMessage(ip, message);
                    } else {
                        print("Client recieved: " + message);//
                    }
                }
            }
            in.close();
        } catch (Exception e) {
            print("Error with input stream: " + e);
            disconnect();
        }

    }

我该如何解决这个问题?

编辑:

这是我服务器类中发送消息的代码: out.println(message); out.flush(); 只要我往消息里面添加了一些内容,就会在循环中执行这段代码。循环结束后,out会被关闭。


1
你是否在服务器的写入器上调用了 flush()close()?如果没有,消息将被缓冲,只有当缓冲区满时才会发送。在此之前,你的客户端将不会收到任何东西。 - king_nak
1
目前,每当服务器写入任何内容时,我都会调用flush()。 - Sadface
4个回答

4
您不应该这样使用ready()javadoc中写道:

"返回:如果下一个read()保证不会阻塞输入,则为True,否则为False。请注意,返回false并不保证下一个读取将被阻止。"

您的代码隐含地假设ready()->false意味着下一个read将被阻止。实际上,它意味着下一个read可能会阻止或不会阻止。

正如@EJP所说...只需进行read调用即可。


我该怎么做才能防止阻塞呢?如果阻塞了,客户端将无法发送任何内容

如果在读取中出现阻塞问题,则可以使用单独的线程进行读取,或更改代码以使用NIO通道选择器。


我明白了。但是我该怎么做才能防止阻塞呢?如果被阻塞,客户端将无法发送任何内容。 - Sadface

2

只需删除in.ready()测试即可。它没有帮助你。readLine()将阻塞,直到有可用数据。如果尚未到达数据,您还计划做什么其他事情?


我正在从服务器发送响应,但由于某些原因它从未被接收。这个客户端能够发送消息,但无法接收。 - Sadface
是的,客户端能够成功发送消息并且服务器也能够成功接收消息。但是,从服务器到客户端的连接似乎不太稳定。有时客户端会在“in.readLine();”处卡住。 - Sadface
@Sadface - 你没有回答 @EJP 的(修辞性)问题。 - Stephen C
客户端在in.readLine()处冻结的问题意味着它无法发送。它基本上已经死了。这意味着,即使我还没有收到任何东西,我可能仍想发送。 - Sadface
@Sadface 客户端在 readLine() 处冻结的问题是没有可读取的行。ready() 返回 false 恰好表示相同的条件。您似乎在应用程序协议方面存在问题。解决方案是修复它。 - user207421
@Sadface 修复什么?如果发送者没有发送任何内容,那就没有可读的内容。你无法修复这个问题,只能等待。 - user207421

1

我想到了三件事:

  • 你在每个 receive 调用中重新打开输入流,并将其包装到一个 BufferedReader 中。这可能会将多个行读入缓冲区,完成(关闭它)后,剩余的缓冲字节将不再可用于后续的 receive 调用。
  • 你考虑过为读取服务器消息使用独立线程吗?在那里,如果被阻塞,它不会有影响。
  • 我在关闭写入数据的套接字一侧并立即关闭它时遇到了一些问题。尽管调用了 flush()close(),但有时其他一侧没有收到所有数据。也许这也是你情况下面临的问题。

编辑:
简单地在 receive 方法之外保留 in 引用将无法完全解决您的问题。您应该使用 while 循环来读取所有缓冲消息,并为每个人调用监听器,例如:

if (in.ready()) {
    String message;
    while ((message = in.readLine()) != null) {
        // ...
    }
 }

但要注意,最后一行可能是部分读取的消息(例如,缓冲了3个半消息)。如果这是一个问题,您可以逐个字符地读取消息以确定何时结束一行,并使用PushbackReader将不完整的消息放回。


我明白了。您建议我将InputReader保留在Receive方法之外吗?在发送消息后,我是否还应该关闭服务器上的输出?目前,我将其保持打开状态,直到不再需要它。 - Sadface
是的,在接收方法之外应该保留输入读取器。在服务器端,我指的是在写完大消息后不要立即关闭套接字。因此保留输出流是完全可以的。 - king_nak

0

你可能需要调用 out.flush() 来刷新 BufferedWriter 中的任何内容。


in.flush(); 显然不存在。 - Sadface
在服务器端,写入数据到 out 后,请调用 out.flush()。 - secmask

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