Java InputStream阻塞读取

时间:2009-03-04 18:06:18

标签: java blocking inputstream rxtx java-io

根据java api,InputStream.read()被描述为:

  

如果没有可用的字节,因为   已到达流的末尾,   返回值-1。这种方法   块直到输入数据可用,   检测到流的末尾,或   抛出异常。

我有一个while(true)循环执行读取,当通过流发送任何内容时,我总是得到-1。这是预期的。

我的问题是什么时候会读取()阻止?因为如果它没有获得任何数据,则返回-1。我希望阻塞读取等到收到数据。如果你已到达输入流的末尾,不应该读取()只是等待数据而不是返回-1?

或者,如果有另一个线程访问流并且read()无法访问流,read()是否仅阻止?


这引出了我的下一个问题。我曾经有过事件监听器(由我的库提供),当数据可用时会通知我。当我收到通知时,我会调用while((aByte = read()) > -1)存储该字节。当我在非常接近的时间内收到两个事件并且我的所有数据都没有被显示时,我感到很困惑。似乎只会显示第二个事件数据的尾端而其余部分将丢失。

我最终改变了我的代码,以便当我收到一个事件时,我调用了if(inputStream.available() > 0) while((aByte = read()) > -1)来存储字节。现在它正常工作,我的所有数据都显示出来了。

有人可以解释这种行为吗?据说InputStream.available()在阻塞下一个调用者(流的?)之前返回你可以读取的字节数。即使我不使用.available(),我希望第一个事件的读取只是阻止第二个事件的读取,但不会擦除或消耗过多的流数据。为什么这样做会导致我的所有数据都不显示?

7 个答案:

答案 0 :(得分:44)

InputStream的某些实现的基础数据源可以表示已到达流的末尾,并且不会再发送数据。在接收到该信号之前,对这种流的读操作可能会阻塞。

例如,来自InputStream套接字的Socket将阻止而不是返回EOF,直到收到设置了FIN标志的TCP数据包为止。当从这样的流接收到EOF时,可以确保在该套接字上发送的所有数据都已被可靠地接收,并且您将无法再读取任何数据。 (如果阻塞读取导致异常,另一方面,某些数据可能已丢失。)

其他流(如来自原始文件或串行端口的流)可能缺少类似的格式或协议,表明不再有可用的数据。当没有数据当前可用时,这些流可以立即返回EOF(-1)而不是阻塞。但是,如果没有这样的格式或协议,您无法确定对方何时完成发送数据。


关于你的第二个问题,听起来你可能遇到了竞争条件。没有看到有问题的代码,我猜这个问题实际上存在于你的“显示”方法中。也许第二次通知显示的尝试在某种程度上破坏了第一次通知期间所做的工作。

答案 1 :(得分:16)

如果流结束,则返回-1。如果流仍然打开(即套接字连接),但没有数据到达读取端(服务器很慢,网络很慢,......)read()阻塞。

您不需要呼叫()。我很难理解您的通知设计,但除了read()本身之外,您不需要任何调用。可用方法()仅为方便起见。

答案 2 :(得分:13)

好的,这有点乱,所以首先要澄清一下:InputStream.read()阻塞与多线程无关。如果您有多个线程从同一输入流读取并且您触发两个彼此非常接近的事件 - 每个线程尝试使用一个事件然后您就会损坏:第一个要读取的线程将获得一些字节(可能全部字节),当第二个线程被调度时,它将读取剩余的字节。如果您计划在多个线程中使用单个IO流,则在某些外部约束上始终synchronized() {}

其次,如果您可以从InputStream读取,直到获得-1然后等待并且稍后可以再次阅读,那么您正在使用的InputStream实现就会被破坏! InputStream的合同明确规定,当没有更多数据要读取时,InputStream.read()应该只返回-1,因为已经达到了整个流的末尾,并且没有更多的数据可用 - 就像当你从文件中读取并到达目的地时。

“现在没有更多数据可用,请等待,你会得到更多”的行为是read()阻止,直到有一些数据可用(或抛出异常)才会返回。< / p>

答案 3 :(得分:6)

默认情况下,提供的RXTX InputStream的行为不符合。

您必须将接收阈值设置为1并禁用接收超时:

serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();

来源:RXTX serial connection - issue with blocking read()

答案 4 :(得分:4)

埃!不要放弃你的流而是Jbu。 我们在这里谈论串行通信。对于串行内容,绝对应该在读取时返回-1可以/,但仍然期望稍后的数据。 问题是,大多数人习惯于处理TCP / IP,除非TCP / IP断开连接,否则应始终返回0 ...然后是啊,-1是有道理的。 但是,对于Serial,长时间没有数据流,没有“HTTP Keep Alive”或TCP / IP heartbeat,或者(在大多数情况下)没有硬件流控制。但链接是物理的,仍然通过“铜”连接,仍然完美无缺。

现在,如果他们说的是正确的,即:Serial应该在-1上关闭,那么我们为什么要注意OnCTS,pmCarroerDetect,onDSR,onRingIndicator等等... 哎呀,如果0表示它在那里,-1表示它没有,那么拧紧所有这些检测功能! : - )

您可能遇到的问题可能在其他地方。

现在,详细说明:

问:“似乎只会显示第二个事件数据的尾端而剩下的就会丢失。”

答:我猜你是在循环中,重新使用相同的byte []缓冲区。第一条消息进来,屏幕上没有显示/ log / std out(因为你在循环中),然后你读取第二条消息,替换缓冲区中的第一条消息数据。再一次,因为我猜你没有存储多少钱,然后确保将你的存储缓冲区偏移了之前的读取量。

问:“我最终改变了我的代码,以便当我收到一个事件时,我会调用if(inputStream.available()&gt; 0)while((aByte = read())&gt; -1)存储字节。“

答:布拉沃......那里有好东西。现在,你的数据缓冲区在IF语句中,你的第二条消息不会破坏你的第一个......好吧,实际上,它可能只是第一个地方的一条大(呃)消息。但现在,您将一次性阅读所有内容,保持数据完整。

C:“......竞争条件......”

A:啊,好的'抓住所有的scape山羊!竞争条件...... :-)是的,这可能是一场竞争条件,事实上它可能已经存在。但是,它也可能只是RXTX清除旗帜的方式。清除“数据可用标志”可能不会像预期的那样快。例如,任何人都知道读取VS readLine与清除先前存储数据的缓冲区和重新设置事件标志之间的区别?我也没有:--)我也找不到答案......但是......让我絮絮叨叨地说几句话。事件驱动的编程仍然存在一些缺陷。让我举一个我最近必须处理的真实世界的例子。

  • 我得到了一些TCP / IP数据,比方说,20个字节。
  • 所以我收到了OnEvent for Received Data。
  • 即使在20个字节上也开始'读'。
  • 在我读完20个字节之前......我得到另外10个字节。
  • 然而,TCP / IP看起来通知我,哦,看到该标志仍然是SET,并且不会再次通知我。
  • 但是,我读完了20个字节(可用()表示有20个字节)...
  • ...最后10个字节保留在TCP / IP Q中......因为我没有得到通知。

看,通知被错过,因为标志仍然设置...即使我已经开始读取字节。如果我完成了字节,那么标志就会被清除,我会收到接下来10个字节的通知。

与你现在发生的情况正好相反。

所以,请使用IF可用()...读取返回的数据长度。 然后,如果你是偏执狂,设置一个计时器并再次调用available(),如果还有数据,那么不读新数据。如果available()返回0(或-1),那么请放松......坐下来......等待下一个OnEvent通知。

答案 5 :(得分:2)

InputStream只是一个抽象类,不幸的是,实现决定了会发生什么。

如果找不到任何内容会发生什么:

  • 套接字(即SocketInputStream)将阻止,直到收到数据(默认情况下)。但是可以设置超时(参见:setSoTimeout),然后read将阻止x ms。如果仍未收到任何内容,则会抛出 SocketTimeoutException

    但无论有没有超时,从SocketInputStream 读取有时会产生-1例如当多个客户端同时连接到同一个{{ 1}},然后即使设备似乎已连接,host:port的结果也可以立即恢复read(永不返回数据)。

  • Serialio 通讯将始终返回-1;您还可以设置超时(使用-1),setTimeoutRx将首先阻止x ms,但如果找不到任何内容,结果仍为read(备注:但是有多个串行io类可用,行为可能依赖于供应商。)

  • 文件(读者或流)会产生 -1

致力于通用解决方案:

  • 如果您在 EOFException 中包含上述任何流,则可以使用DataInputStreamreadByte等方法。 所有readChar值都会转换为-1 (PS:如果您执行大量小读取,那么将它包装在{{}中是一个好主意。 1}}首先)
  • EOFExceptionBufferedInputStream都延伸SocketTimeoutException,还有其他几种可能的EOFException检查IOException以检测通信问题很方便。

另一个敏感话题是潮红。就套接字而言IOException意味着“立即发送”,但就Serialio而言,它意味着“丢弃缓冲区”。

答案 6 :(得分:-7)

我认为如果使用thread.sleep()

,则可以接收整个数据流