Java InputStream阻塞读取

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

如果由于到达stream的末尾而没有字节可用,则返回值-1。 此方法阻塞,直到input数据可用,stream的末尾被检测到,或抛出exception。

我有一个while(true)循环做一个读取,我总是得到-1,当没有什么是通过stream发送。 这是预料之中的。

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

或者如果有另一个线程访问stream,并且你的read()不能访问这个stream,那么read()只会被阻塞吗?


这导致我的下一个问题。 我曾经有事件监听器(由我的库提供)在数据可用时通知我。 当我被通知时,我会调用while((aByte = read()) > -1)存储字节。 当我在非常接近的时间里得到两个事件时,我感到困惑,并不是所有的数据都被显示出来。 似乎只显示第二个事件数据的尾部,剩下的部分不见了。

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

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

InputStream一些实现的底层数据源可以表示已经到达stream的末尾,并且不会发送更多的数据。 在接收到这个信号之前,对这样的数据stream的读操作可能会被阻塞。

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

其他stream,如来自原始文件或串行端口的stream,可能缺less类似的格式或协议来指示没有更多数据可用。 当没有数据可用时,这样的stream可以立即返回EOF(-1)而不是阻塞。 然而,在没有这种格式或协议的情况下,您不能确定对方何时发送数据。


关于你的第二个问题,这听起来像你可能有一个竞争条件。 没有看到代码的问题,我猜这个问题实际上是在你的“显示”的方法。 也许第二次通知显示的尝试是以某种方式破坏第一次通知期间完成的工作。

它返回-1,如果它是stream的结束。 如果stream仍然打开(即套接字连接),但没有数据到达读取端(服务器速度慢,networking速度慢,…)read()块。

你不需要呼叫可用()。 我很难理解你的通知devise,但是除了read()本身,你不需要任何的调用。 方法可用()只是为了方便。

好吧,这是一个混乱,所以首先让我们清楚:InputStream.read()阻塞与multithreading无关。 如果你有多个线程从同一个inputstream中读取,而且你触发了两个非常接近的事件 – 每个线程试图消耗一个事件,那么你会得到腐败:第一个线程读取将获得一些字节(可能所有字节),当第二个线程被调度时,它将读取其余的字节。 如果您计划在多个线程中使用单个IOstream,则总是在某种外部约束条件下进行同步。

其次,如果你可以从你的InputStream读取,直到你得到-1,然后等待,并且可以稍后再读取,那么你正在使用的InputStream实现被破坏! InputStream的合约明确指出,当没有更多的数据要读取时,InputStream.read()应该只返回-1,因为整个stream的结束已经到达,没有更多的数据可用 – 就像当你从一个文件,你到达最后。

“现在没有更多的数据可用,请等待,你会得到更多”的行为是为了read()来阻塞,并且在有一些数据可用(或抛出exception)之前不会返回。

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

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

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

来源: RXTX串行连接 – 阻塞读取问题()

埃! 不要放弃你的stream,但不要放弃。 我们在这里说串口通讯。 对于串行的东西,绝对期望-1读取可以被返回,但仍然期望在稍后的时间。 问题是,大多数人习惯于处理TCP / IP应该总是返回一个0,除非TCP / IP断开…然后是,-1是有意义的。 但是,使用串口时,数据stream不会延长一段时间,也不会有“HTTP Keep Alive”或TCP / IP心跳,或(在大多数情况下)没有硬件stream量控制。 但这个链接是实体的,仍然通过“铜”连接起来,仍然完美地生活着。

现在,如果他们说的是正确的,即:串行应该被closures-1,那么为什么我们必须注意的东西像OnCTS,pmCarroerDetect,onDSR,onRingIndicator等…哎,如果0意味着它的存在,-1表示不是,然后拧所有的检测function! 🙂

您可能面临的问题可能在其他地方。

现在,具体到:

问:“似乎只有第二项赛事数据的尾部会显示出来,其余的则不见了。”

A:我会猜测你是在一个循环中,重新使用相同的byte []缓冲区。 第一条消息进入,没有显示在屏幕/日志/标准输出中(因为你在循环中),那么你读取第二条消息,replace缓冲区中的第一条消息数据。 再一次,因为我猜测你没有存储多less读,然后确保以前面的读取量来抵消你的存储缓冲区。

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

答:布拉沃…多数民众赞成那里的好东西。 现在,你的数据缓冲区在一个IF语句中,你的第二个消息不会打断你的第一个…实际上,它可能只是第一个地方的一个大的消息。 但现在,您将一眼就读完,保持数据不变。

C:“……种族条件…”

– 答:啊,好醇“赶上所有的景观山羊! 比赛条件… :-)是的,这可能是一个竞争条件,事实上它可能是一直很好的。 但是,这也可能是RXTX清除标志的方式。 “数据可用标志”的清除可能不会像预期的那样快。 例如,任何人都知道读取VS readLine和清除缓冲区之间的区别,数据先前存储在这个区域中,并重新设置事件标志? 我也没有。:-)我也没有find答案,但是…让我多讲几句话。 事件驱动的编程仍然有一些缺陷。 让我给你一个我最近不得不面对的现实世界的例子。

  • 我有一些TCP / IP数据,可以说,20个字节。
  • 所以我收到了接收数据的OnEvent。
  • 我甚至在20个字节上开始“读”。
  • 在我读完我的20个字节之前,我又得到了10个字节。
  • 然而,TCP / IP看起来通知我,哦,看到这个标志还是SET,而不会再通知我。
  • 但是,我完成阅读我的20个字节(可用()表示有20)…
  • …和最后10个字节保留在TCP / IP Q …因为我没有通知他们。

看,通知是错过的,因为国旗仍然设置…即使我已经开始阅读字节。 如果我完成了字节,那么标志将被清除,并且我将接收到接下来的10个字节的通知。

与你现在正在发生的事情完全相反。

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

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

如果没有发现什么会发生什么:

  • 套接字 (即SocketInputStream )将阻塞,直到收到数据(默认)。 但可以设置超时(请参阅: setSoTimeout ),然后read将会阻塞x毫秒。 如果仍然没有收到一个SocketTimeoutException将被抛出。

    但有或没有超时,从SocketInputStream读取有时可能会导致-1例如,当多个客户端同时连接到同一个host:port ,即使这些设备似乎连接, read的结果可能会立即在-1 (不会返回数据)中恢复。

  • Serialio通信将始终返回-1 ; 您也可以设置超时(使用setTimeoutRx ), read将首先阻塞x毫秒,但如果没有find结果,结果仍然是-1(注:但有多个串行io类可用,行为可能是供应商的依赖。)

  • 文件 (读取器或stream)将导致EOFException

工作到一个通用的解决scheme:

  • 如果你在DataInputStream封装了上述任何一个stream,那么你可以使用像readBytereadChar等方法。 所有-1值都转换为EOFException (PS:如果你执行了很多小的读取,那么最好先把它包装在一个BufferedInputStream
  • SocketTimeoutExceptionEOFException扩展IOException ,还有其他几种可能的IOException检查IOException是否检测通信问题是很方便的。

另一个敏感话题是冲洗。 flush套接字意味着“现在发送”,但是就Serialio而言,它意味着“丢弃缓冲区”。

我使用eclipse Jetty 9.2.2。 我很惊讶地看到,我的请求中的10-12%每个都需要大约500+毫秒,而其他的则在2-3毫秒内处理。 代码部分

 timeBufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream())); String line = null; String postData = ""; while ((line = br.readLine()) != null){ // this is the line taking all time postData+=line; } br.close(); 

我想如果你使用thread.sleep(),你可以接收整个数据stream