【问题标题】:Buffered Reader for a socket is never ready套接字的缓冲读取器永远不会准备好
【发布时间】:2017-10-08 21:43:27
【问题描述】:

为了完全透明,这是一个作业。

还有更多工作要做,但目前我只是想获得以下内容:

  1. 节点 A 从文本文件中读取
  2. 节点 A 使用套接字向节点 B 发送文本文件(减去第一行)
  3. 节点 B 从所述套接字读取,并将其打印到控制台

但是,现在看来,信息要么没有被发送,要么节点 B 没有正确读取。

在我的主类中,我这样设置节点:

NodeA nodeA = new NodeA();
NodeB nodeB = new NodeB();

new Thread(nodeA).start();
new Thread(nodeB).start();

在节点 A 中,我这样做:

//Open a socket for talking with NodeB
Socket mySocket = new Socket(InetAddress.getLocalHost(), portNum);

//Set up the socket's output
PrintWriter out = new PrintWriter(mySocket.getOutputStream(), true);

//Loop through the lines of the confA file, writing them to the socket
String line = bufferedReader.readLine();
while (line != null)
{
    //Write the line to the socket, get the next line
    out.println(line); //updated to println, this flushes and fixes another problem
    out.flush();
    line = bufferedReader.readLine();
}

//Close the socket
mySocket.close();

请注意,节点 A 的循环工作正常。当我使用 print 语句进行测试时,它不会永远循环并且会遍历预期的文本行。

然后,在节点 B 端:更新以显示当前节点 B 代码

//Open the socket 
ServerSocket mySocket = new ServerSocket(portNum);
Socket connectionSocket = mySocket.accept();

//Set up a reader on the socket to get what's coming from it
BufferedReader in = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));

String line = in.readLine(); //hang occurs here

while(line != null) {
    System.out.println(line);
    line = in.readLine();;
}

然而,in.ready() 永远不会是真的。我尝试过使用while循环等待这种情况发生,但它从未发生过。

我真的不知道为什么。我不知道我是否正确设置了套接字,是否正确设置了服务器,是否正确监听等等。

我只是认为将 B 设置为正在侦听 A 的服务器是最有意义的。我希望这是对的。它看起来类似于我在 SO 上看到的其他一些示例。

感谢您的所有帮助。我对套接字、端口、监听和其他方面非常不熟悉,所以如果我一开始不明白你的建议,请原谅我。我会尽我所能去理解它。

我没有添加整个代码,希望能使其更具可读性和明确问题所在,但如果您需要更多信息,请随时询问,我会尽力提供。

【问题讨论】:

  • 1) 每次ServerSocket.accept() 返回时,您都会在服务器和客户端之间建立一个连接的套接字。对于 that 连接,您not 再次调用ServerSocket.accept()。 2) 在您的客户端中,PrintWriter 在内部缓冲数据。如果您希望在网络上立即看到调用out.print(line); 的结果,那么您必须调用out.flush() 作为下一条语句。
  • ready() 所做的只是返回下一次读取是否会立即返回。见the documentation

标签: java sockets bufferedreader java-threads


【解决方案1】:

服务器必须先从ServerSocket获取Socket到客户端。

connectionSocket = mySocket.accept();

服务器线程将处于休眠状态,直到客户端使其接受连接套接字。

然后你可以从connectionSocket中读取。 ready 不需要。

因为这是一个任务,我把剩下的交给你。


典型的服务器会这样做:

for (;;) {
    Socket socket = serverSocket.accept();
    ... pass the socket to a thread from a pool of threads
}

【讨论】:

  • 感谢您的回复,但有一些事情仍然很令人困惑。一旦服务器完成等待并且客户端已经连接,我不需要再做一个 .accept() 了,对吧?我尝试更改我的代码以便只有初始的 .accept,但我似乎仍然无法从套接字读取,它只是永远挂在 readLine() 上。
  • 你应该只有一个 ServerSocket.accept 产生一个 Socket 来读取。
  • 谢谢。我已经更新了代码,删除了额外的 .accepts() 和 .ready() ,现在显示挂起发生的位置。
  • 好的,你来了。在客户端执行println 而不是print。这会将缓冲区刷新到输出。 new PrintWriter(..., true) 在 println 上自动刷新。在我身边晚安。
  • 谢谢!它现在似乎工作得很好。我想知道为什么当我尝试 out.flush() 时结果会变成一行。现在我可以删除它了。
【解决方案2】:

我认为问题在于,准备好只是意味着如果您调用读取,它就不会阻塞。如果你在 grepcode 上查找函数,你可以看到被执行的代码:

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/io/BufferedReader.java#BufferedReader.ready%28%29

就绪线程只是意味着它不会阻塞,这在您想确保线程不会被占用但并不能真正告诉您是否有缓冲区时很有用。

您要做的是执行 readline,作为阻塞调用,直到数据被消耗。如果你不希望这阻塞你当前的线程,那么分拆一个新的消费者线程,专门用于这个读取可以阻塞。

此外,请确保您使用已关闭的套接字或刷新来结束发送通信,以便在完成时向消费流指示。并且您只需要在每个打开/关闭会话中接受一次套接字接受。

【讨论】:

  • 我不介意读取阻塞我当前的线程,这根本不是问题。我尝试将我的代码更改为最初只有一个 .accept() 并更改 in.ready() 以确保从 readLine() 返回的行不为空(还有更多行要读入)但是它仍然挂在我尝试做的初始 readLine() 上。
  • Readline 需要结束字符串,它正在寻找 \n EOL 或关闭的套接字。如果它挂起,则意味着它正在等待更多数据。您可以在写入结束时关闭写入器上的套接字作为测试以确认。
  • 谢谢。我测试了一个 mySocket.close();在节点 A 的代码末尾。不过还没有运气。我还需要在另一端关闭它吗?
猜你喜欢
  • 2016-02-14
  • 2013-02-25
  • 2012-09-25
  • 2020-09-20
  • 1970-01-01
  • 2014-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多