Java InputStream阻塞读取

问题描述 投票:50回答:7

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

如果由于到达流末尾而没有可用字节,则返回值-1。此方法将阻塞,直到输入数据可用,检测到流的末尾或抛出异常。

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

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

或者read()只阻止是否有另一个线程访问流并且你的read()无法访问流?


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

我最终改变了我的代码,以便当我得到一个事件时,称为qazxsw poi存储该字节。现在它正常工作,我的所有数据都显示出来了。

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

java blocking inputstream rxtx java-io
7个回答
45
投票

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

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

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


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


17
投票

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

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


13
投票

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

其次,如果您可以从synchronized() {}读取,直到获得-1然后等待并且稍后可以再次读取,那么您正在使用的InputStream实现已经破坏! InputStream的合同明确指出,当没有更多的数据需要读取时,InputStream应该只返回-1,因为已经达到整个流的末尾并且没有更多的数据可用 - 就像你从文件中读取而你到达终点。

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


6
投票

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

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

read()

资料来源:serialPort.enableReceiveThreshold(1); serialPort.disableReceiveTimeout();


4
投票

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

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

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

现在,详细说明:

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

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

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

A:Bravo ......那就是好东西。现在,你的数据缓冲区在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(),如果还有数据,那么不读新数据。如果available()返回0(或-1),则放松......坐下......等待下一个OnEvent通知。


2
投票

RXTX serial connection - issue with blocking read()只是一个抽象类,不幸的是,实现决定了会发生什么。

如果没有找到会发生什么:

  • 套接字(即InputStream)将阻塞,直到收到数据(默认情况下)。但是可以设置超时(参见:SocketInputStream),然后setSoTimeout将阻塞x ms。如果仍然没有收到任何东西,那么将抛出read。 但无论有没有超时,从SocketTimeoutException读取有时会导致SocketInputStream。 (例如,当多个客户端同时连接到相同的-1时,即使设备看起来连接,host:port的结果也可以立即恢复到read(永不返回数据)。)
  • Serialio通信将始终返回-1;您还可以设置超时(使用-1),setTimeoutRx将首先阻止x ms,但如果找不到任何结果,结果仍然是read。 (备注:但是有多个串行io类可用,行为可能依赖于供应商。)
  • 文件(读者或流)将导致-1

致力于通用解决方案:

  • 如果你将任何上述流包装在EOFException中,那么你可以使用像DataInputStreamreadByte等方法。所有readChar值都转换为-1。 (PS:如果你执行了很多小读取,那么首先将它包装在EOFException中是一个好主意)
  • BufferedInputStreamSocketTimeoutException都延伸EOFException,还有其他几种可能的IOException。只需检查IOException来检测通信问题就很方便了。

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


-7
投票

我想如果你使用thread.sleep()你可以收到整个数据流

© www.soinside.com 2019 - 2024. All rights reserved.