【问题标题】:Java InputStream.available() returning > 0 after readingJava InputStream.available() 读取后返回 > 0
【发布时间】:2019-04-18 05:03:21
【问题描述】:

我正在构建一个聊天程序,其中主机通过套接字连接并使用 ObjectInput 和 ObjectOutput 流相互交谈。主机根据键盘输入构建一个字符串,并将其与整数数组一起发送给其他主机。

主机通过 readObject() 成功读取消息后,while(true) 循环继续,并且该主机在下一次调用 readObject() 时挂起。我只能推测这是因为 indata.available() 即使在读取了其中的内容之后也会返回 true,并且当它在发送其他内容之前尝试再次读取时,它会阻塞(等待)。

相关代码的sn-p如下。我做了一些研究,发现我无法刷新或清空输入流。我也无法关闭它 - 由于持续运行的聊天程序的性质,它需要保持打开状态才能继续阅读。

另外,我知道我正在检查 indata.available(),然后使用 inputs.readObject() 读取。我认为这是正确的做法,但如果我错了,请纠正我。

我不知道该怎么办!如果我没有将对象写入流,我需要 indata.available() 返回 0。

    private InputStream[] indata;                
    private ObjectInputStream[] inputs;          
    private ObjectOutputStream[] outputs;        
    private int[] stamps;   

    // Establish connections via sockets between 3 hosts, serverless                     

    while (true) {
        // Build a message
        for (all hosts that aren't myself) {
            if ( i != rank ) {
                outputs[i].writeObject( message );
                outputs[i].writeObject( stamps );
                outputs[i].flush( ); 
                outputs[i].reset( );
            }
        }

        // Read a message in from a host that sent one
        for (all hosts that aren't myself) {
            if (indata[j].available() > 0) {
                String message = (String)inputs[j].readObject();
                int[] senderStamps = (int[])inputs[j].readObject();
            }
        }
    }

一些附加信息,为了澄清:

我使用 available() 是因为讲师在他的代码中使用了它,我不允许更改它。此外,当只发送一个对象(字符串)时,对 available() 的调用按预期工作 - 发送端的唯一代码是“writeObject”和“flush”。添加代码以发送数组是我的工作,当我这样做时,我还必须添加代码以 reset() ObjectOutputStream(或者我遇到其他问题 - 发送、修改和发送数组时再次在发送之间不调用 reset(),则发送原始的、未修改的版本而不是新修改的版本)。

我不能只阻塞读取,因为读取阻塞的进程无法写入其他主机,即使主机没有可读取的内容,我也需要能够写入。

另外,我们不允许使用多个线程。

【问题讨论】:

  • InputStream::available 总是返回 0。这个方法的javadocs说'这个方法应该被子类覆盖'。那么你能发布一个真正实现的indata吗?此外,您从inputs 读取数据。你能发布indatainputs 的关系吗?
  • 我想知道你是否可以做一个 (str = inputs[j].readObject()) != null) 作为你的检查
  • @AnarAmrastanov 它被 OP 已经在使用的子类覆盖,例如 ObjectInputStream。阅读问题。
  • 这里唯一的问题是您使用的是available()。简单的回答:不要。这种方法的正确用法很少(如果有的话),这不是其中之一。 一些 字节可用这一事实永远不能保证整个对象 可用,因此您将总是冒着被readObject() 阻塞的风险。所以就这样做吧。堵塞。为每个套接字指定一个线程。
  • @Benson99 不,他不能。当整个对象不可用时,readObject() 不会返回 null。请参阅 Javadoc。

标签: java java-io blocking objectinputstream


【解决方案1】:

我知道发生了什么,但我不确定我明白为什么。

当我在发送后调用reset(),然后在接收端读取对象时,流中还剩下1个字节。这就是导致我陷入 if 子句然后再次阅读时阻塞的原因(因为真的没有要阅读的对象)。

我不得不调用reset,因为我正在发送一个持久对象(数组)。我注意到我只发送字符串时不必调用reset,字符串和数组之间的区别在于数组是类的数据成员,而每次循环都重新创建字符串跑了。

所以,我创建了一个我想要发送的数组的非持久副本,并发送了该副本。当我这样做时,我不必调用重置(仍然不明白为什么)。此外,从输入流中读取后,内部还剩下 0 个字节,因此它永远不会将程序置于 readObject 会阻塞的位置。

我想我有点理解为什么我在发送非持久对象时不必重置,但我不明白为什么 reset() 会导致数据留在相应的输入流中。

无论哪种方式,它现在都按预期工作。

【讨论】:

  • 我也不懂。 reset() 不会在流中留下额外的字节。它确实发送了一些东西,但ObjectInputStream 在后台读取了所有内容。一定有别的东西在这样做。你确定你没有在某处写入底层流吗?你也可以调查ObjectOutputStream.writeUnshared()。在任何情况下,潜在的问题仍然可能发生,因为不能保证整个对象会立即到达。
  • 是的,这很奇怪。当我发送持久对象然后 reset() 时,available 返回 1。当我发送非持久对象时,它返回 0。是的,我确定我不会在其他任何地方写信给它。没有那么多代码行需要梳理,但是,仅仅改变 reset 的使用(而不是别的)就可以解决问题,所以这可能证明我没有在其他地方写它。
猜你喜欢
  • 2021-03-28
  • 2011-08-15
  • 2023-03-13
  • 1970-01-01
  • 2019-02-19
  • 1970-01-01
  • 1970-01-01
  • 2018-03-07
  • 1970-01-01
相关资源
最近更新 更多