简体   繁体   English

Java InputStream 阻塞读

[英]Java InputStream blocking read

According to the java api, the InputStream.read() is described as:根据 java api, InputStream.read()被描述为:

If no byte is available because the end of the stream has been reached, the value -1 is returned.如果由于到达 stream 的末尾而没有可用的字节,则返回值 -1。 This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.此方法阻塞,直到输入数据可用、检测到 stream 的结尾或抛出异常。

I have a while(true) loop doing a read and I always get -1 when nothing's sent over the stream.我有一个while(true)循环进行读取,当没有任何内容通过 stream 发送时,我总是得到 -1。 That's expected.这是预期的。

My question is when would read() ever block?我的问题是 read() 什么时候会阻塞? Since if it doesn't get any data it returns -1.因为如果它没有得到任何数据,它会返回-1。 I would expect a blocking read to wait until data is received.我希望阻塞读取要等到收到数据。 If you've reached the end of the input stream, shouldn't read() simply wait for data instead of returning -1?如果您已经到达输入 stream 的末尾,那么 read() 不应该只是等待数据而不是返回 -1 吗?

Or does read() only block if there's another thread accessing the stream and your read() cannot access the stream?或者 read() 只有在有另一个线程访问 stream 并且您的 read() 无法访问 stream 时才会阻塞?


Which leads me to my next question.这引出了我的下一个问题。 I used to have event listener (provided by my library) that would notify me when data is available.我曾经有事件监听器(由我的库提供),它会在数据可用时通知我。 When I was notified I would call while((aByte = read()) > -1) store the byte.当我收到通知时,我会调用while((aByte = read()) > -1)存储字节。 I was puzzled when I'd get TWO events in very close time proximity and not all my data was being displayed.当我在非常接近的时间内获得两个事件并且并未显示我的所有数据时,我感到很困惑。 It seemed like only the tail end of the second event's data would be displayed and the the rest was missing.似乎只显示第二个事件数据的尾部,并且缺少 rest。

I eventually changed my code so that when I get an event I'd called if(inputStream.available() > 0) while((aByte = read()) > -1) store the byte.我最终更改了我的代码,以便当我收到一个事件时,我调用了if(inputStream.available() > 0) while((aByte = read()) > -1)存储字节。 Now it worked properly and all my data was displayed.现在它工作正常,我的所有数据都显示出来了。

Can someone explain this behavior?有人可以解释这种行为吗? The InputStream.available() is said to return the number of bytes you can read before blocking the next caller (of the stream?).据说InputStream.available()返回在阻塞下一个调用者(stream?)之前可以读取的字节数。 Even if I don't use.available() I would expect the read of the first event to just block the read of the second event, but not erase or consume too much stream data.即使我不使用.available(),我也希望读取第一个事件只会阻止读取第二个事件,但不会擦除或消耗太多 stream 数据。 Why would doing this cause not all of my data to be displayed?为什么这样做会导致无法显示我的所有数据?

The underlying data source for some implementations of InputStream can signal that the end of the stream has been reached, and no more data will be sent. InputStream的某些实现的底层数据源可以发出信号,表明 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 已到达末尾,将不再发送数据。 Until this signal is received, read operations on such a stream can block.在收到此信号之前,对此类 stream 的读取操作可能会阻塞。

For example, an InputStream from a Socket socket will block, rather than returning EOF, until a TCP packet with the FIN flag set is received.例如,来自Socket套接字的InputStream将阻塞,而不是返回 EOF,直到收到设置了 FIN 标志的 TCP 数据包。 When EOF is received from such a stream, you can be assured that all data sent on that socket has been reliably received, and you won't be able to read any more data.当从这样的 stream 接收到 EOF 时,您可以确信在该套接字上发送的所有数据都已可靠接收,您将无法读取更多数据。 (If a blocking read results in an exception, on the other hand, some data may have been lost.) (另一方面,如果阻塞读取导致异常,则可能会丢失一些数据。)

Other streams, like those from a raw file or serial port, may lack a similar format or protocol to indicate that no more data will be available.其他流,例如来自原始文件或串行端口的流,可能缺少类似的格式或协议,以表明没有更多数据可用。 Such streams can immediately return EOF (-1) rather than blocking when no data are currently available.当当前没有数据可用时,此类流可以立即返回 EOF (-1) 而不是阻塞。 In the absence of such a format or protocol, however, you can't be sure when the other side is done sending data.但是,在没有这种格式或协议的情况下,您无法确定对方何时完成发送数据。


With regard to your second question, it sounds like you may have had a race condition.关于你的第二个问题,听起来你可能有比赛条件。 Without seeing the code in question, I'm guessing that the problem actually lay in your method of "display".在没有看到有问题的代码的情况下,我猜测问题实际上在于您的“显示”方法。 Perhaps the attempt to display by the second notification was somehow clobbering the work done during the first notification.也许第二个通知显示的尝试以某种方式破坏了第一个通知期间完成的工作。

It returns -1 if it's end of stream.如果它是 stream 的结尾,则返回 -1。 If stream is still open (ie socket connection) but no data has reached the reading side (server is slow, networks is slow,...) the read() blocks.如果 stream 仍然打开(即套接字连接)但没有数据到达读取端(服务器速度慢,网络速度慢,...) read() 阻塞。

You don't need call available().你不需要调用available()。 I have a hard time understanding your notification design, but you don't need any calls except read() itself.我很难理解您的通知设计,但是除了 read() 本身之外,您不需要任何调用。 Method available() is there for convenience only.方法 available() 只是为了方便。

OK, this is a bit of a mess so first thing lets clear this up: InputStream.read() blocking has nothing to do with multi-threading.好的,这有点乱,所以首先让我们澄清一下: InputStream.read()阻塞与多线程无关。 If you have multiple threads reading from the same input stream and you trigger two events very close to each other - where each thread is trying to consume an event then you'd get corruption: the first thread to read will get some bytes (possibly all the bytes) and when the second thread gets scheduled it will read the rest of the bytes.如果您有多个线程从同一个输入 stream 读取,并且您触发了两个彼此非常接近的事件 - 每个线程都试图消耗一个事件,那么您会得到损坏:第一个读取的线程将获得一些字节(可能全部字节),当第二个线程被调度时,它将读取字节的 rest。 If you plan to use a single IO stream in more then one thread, always synchronized() {} on some external constraint.如果您计划在多个线程中使用单个 IO stream,则始终在某些外部约束上使用synchronized() {}

Second, if you can read from your InputStream until you get -1 and then wait and can read again later, then the InputStream implementation you are using is broken!其次,如果您可以从InputStream中读取直到获得 -1,然后等待并稍后再读取,那么您正在使用的 InputStream 实现已损坏! The contract for InputStream clearly states that an InputStream.read() should only return -1 when there is no more data to read because the end of the stream has been reached and no more data will EVER be available - like when you read from a file and you reach the end (1) . InputStream的合同明确规定InputStream.read()只应在没有更多数据要读取时返回 -1,因为 stream 的末尾已经到达并且不再有数据可用 - 就像你从文件,你到达终点(1)

The behavior for "no more data is available now, please wait and you'll get more" is for read() to block and not return until there is some data available (or an exception is thrown). “现在没有更多数据可用,请稍候,你会得到更多”的行为是read()阻塞并且在有一些数据可用(或抛出异常)之前不返回。

  1. As noted deep in the discussion on erickson's (currently top) answer , a FileInputStream implementation can actually read past the "end of file" and provide more data after a read() has returned -1 - if data is added to the file later.正如在对erickson(目前最热门)答案的讨论中所指出的那样, FileInputStream实现实际上可以读取“文件末尾”并在read()返回-1后提供更多数据 - 如果稍后将数据添加到文件中。 This is an edge case and is basically the only such case in common InputStream implementations (or at worst - very very rare).这是一个边缘情况,基本上是常见InputStream实现中唯一的这种情况(或者在最坏的情况下 - 非常非常罕见)。 You should take that into account if you know you use FileInputStream and expect the file you read from have additional data added (a common example is tailing a log file), but otherwise it is just a deficiency of the InputStream API and in any case - you'd be better off if you can stop using the java.io style blocking IO APIs and use java.nio non-blocking IO APIs.如果您知道您使用FileInputStream期望从中读取的文件添加了其他数据(一个常见的示例是拖尾日志文件),则应该考虑到这一点,但否则它只是InputStream API 的缺陷,无论如何 - you'd be better off if you can stop using the java.io style blocking IO APIs and use java.nio non-blocking IO APIs.

By default the behavior of the provided RXTX InputStream is not compliant.默认情况下,提供的 RXTX InputStream 的行为是不兼容的。

You have to set the receive threshold to 1 and disable the receive timeout:您必须将接收阈值设置为 1 并禁用接收超时:

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

Source: RXTX serial connection - issue with blocking read()来源: RXTX 串行连接 - 阻塞 read() 的问题

Aye.是的。 Don't give up on your stream yet Jbu.不要放弃你的 stream 还是 Jbu。 We are talking Serial communication here, For serial stuff, it is absolutely expected that a -1 can/will be returned on reads.我们在这里谈论的是串行通信,对于串行的东西,绝对可以/将在读取时返回-1。 yet still expect data at a later time.但仍期待稍后的数据。 The problem is that most people are used to dealing with TCP/IP which should always return a 0 unless the TCP/IP disconnected.., then yea.问题是大多数人习惯于处理 TCP/IP,除非 TCP/IP 断开连接,否则它应该始终返回 0..,然后是的。 -1 makes sense, However, with Serial there is no data flow for extended periods of time, and no "HTTP Keep Alive", or TCP/IP heartbeat. -1 是有道理的,但是,对于 Serial,长时间没有数据流,也没有“HTTP Keep Alive”或 TCP/IP 心跳。 or (in most cases) no hardware flow control, But the link is physical.或者(在大多数情况下)没有硬件流控制,但链路是物理的。 and still connected by "copper" and still perfectly live.并且仍然通过“铜”连接并且仍然完美地活着。

Now, if what they are saying is correct, ie: Serial should be closed on a -1, then why do we have to watch for stuff like OnCTS, pmCarroerDetect, onDSR, onRingIndicator, etc... Heck, if 0 means its there, and -1 means its not, then screw all those detection functions: :-)现在,如果他们所说的是正确的,即:串行应该在 -1 上关闭,那么为什么我们必须注意诸如 OnCTS、pmCarroerDetect、onDSR、onRingIndicator 等内容......哎呀,如果 0 意味着它在那里, -1 表示不是,然后拧所有这些检测功能::-)

The problem you may be facing may lay elsewhere.您可能面临的问题可能在其他地方。

Now, onto specifics:现在,到细节:

Q: "It seemed like only the tail end of the second event's data would be displayed and the the rest was missing."问:“似乎只显示第二个事件数据的尾部,并且缺少 rest。”

A: I'm going to guess that you were in a loop, re-using the same byte[] buffer.答:我猜你是在循环中,重复使用相同的 byte[] 缓冲区。 1st message comes in, is not displayed on the screen/log/std out yet (because you are in the loop), then you read the 2nd message, replacing the 1st message data in the buffer.第一条消息进来,还没有显示在屏幕/日志/标准输出上(因为你在循环中),然后你读取第二条消息,替换缓冲区中的第一条消息数据。 Again, because I'm going to guess that you don't store how much you read, and then made sure to offset your store buffer by the previous read amount.再说一次,因为我猜你不会存储你读了多少,然后确保将你的存储缓冲区偏移上一个读取量。

Q:"I eventually changed my code so that when I get an event I'd called if(inputStream.available() > 0) while((aByte = read()) > -1) store the byte."问:“我最终更改了我的代码,以便当我得到一个事件时,我调用了 if(inputStream.available() > 0) while((aByte = read()) > -1) 来存储字节。”

A: Bravo... thats the good stuff there. A:好极了……这就是好东西。 Now, you data buffer is inside an IF statement, your 2nd message will not clobber your 1st... well, actually, it was probably just one big(er) message in the 1st place.现在,您的数据缓冲区位于 IF 语句中,您的第二条消息不会破坏您的第一条消息……好吧,实际上,它可能只是第一个位置的一条大(er)消息。 But now, you will read it all in one shot, keeping the data intact.但现在,您将一口气读完所有内容,并保持数据完整。

C: "... race condition..." C:“……比赛条件……”

A: Ahhh, the good ol' catch all scape goat. A: 啊,好家伙抓住所有的替罪羊。 The race condition..: ,-) Yes, this may have been a race condition.竞争条件..:,-) 是的,这可能是竞争条件。 in fact it may have well been, But.事实上,它可能是,但是。 it could also just be the way the RXTX clears the flag.它也可能只是 RXTX 清除标志的方式。 The clearing of the 'data available flag' may not happen as quick as one expects, For example?清除“数据可用标志”可能不会像预期的那样快,例如? anyone know the difference between read VS readLine in relation to clearing the buffer the data was previously stored in and re-setting the event flag.任何人都知道 read VS readLine 与清除先前存储数据的缓冲区和重新设置事件标志之间的区别。 Neither do I: .-) Nor can I find the answer yet... but... let me ramble on for a few sentences more.我也没有:.-) 我也找不到答案……但是……让我再啰嗦几句。 Event driven programming still has some flaws.事件驱动编程仍然存在一些缺陷。 Let me give you a real world example I had to deal with recently.让我给你一个我最近不得不处理的真实世界的例子。

  • I got some TCP/IP data, lets say, 20 bytes.我得到了一些 TCP/IP 数据,比如说 20 个字节。
  • So I receive the OnEvent for Received Data.所以我收到了接收数据的 OnEvent。
  • I start my 'read' even on the 20 bytes.我什至在 20 个字节上开始我的“读取”。
  • Before I finish reading my 20 bytes... I get another 10 bytes.在我读完我的 20 个字节之前……我又得到了 10 个字节。
  • The TCP/IP however, looks to notify me, oh, sees that the flag is still SET, and will not notify me again.然而,TCP/IP 看起来会通知我,哦,看到标志仍然是 SET,并且不会再次通知我。
  • However, I finish reading my 20 bytes (available() said there were 20)...但是,我读完了我的 20 个字节(available() 说有 20 个)...
  • ... and the last 10 bytes remain in the TCP/IP Q... because I was not notified of them. ...最后 10 个字节保留在 TCP/IP Q... 因为我没有收到通知。

See, the notification was missed because the flag was still set... even though I had begun reading the bytes.看,通知被错过了,因为标志仍然设置......即使我已经开始读取字节。 Had I finished the bytes, then the flag would have been cleared, and I would have received notification for the next 10 bytes.如果我完成了字节,那么标志就会被清除,并且我会收到接下来 10 个字节的通知。

The exact opposite of what is happening for you now.与你现在正在发生的事情完全相反。

So yea, go with an IF available()... do a read of the returned length of data.所以是的,go 与 IF available()... 读取返回的数据长度。 Then, if you are paranoid, set a timer and call available() again, if there is still data there, then do a read no the new data.然后,如果您偏执,请设置一个计时器并再次调用 available(),如果那里仍有数据,则不读取新数据。 If available() returns 0 (or -1), then relax... sit back... and wait for the next OnEvent notification.如果 available() 返回 0(或 -1),则放松...坐等...等待下一个 OnEvent 通知。

InputStream is just an abstract class , unfortunately the implementation decides what happens. InputStream只是一个抽象的 class ,不幸的是实现决定了会发生什么。

What happens if nothing is found:如果什么都没找到会发生什么:

  • Sockets (ie SocketInputStream ) will block until data is received (by default). Sockets (即SocketInputStream )将阻塞直到接收到数据(默认情况下)。 But it's possible to set a timeout (see: setSoTimeout ), then the read will block for x ms.但是可以设置超时(参见: setSoTimeout ),然后read将阻塞 x 毫秒。 If still nothing is received then a SocketTimeoutException will be thrown.如果仍然没有收到任何内容,则会抛出SocketTimeoutException

    But with or without timeout, reading from a SocketInputStream can sometimes result in a -1 .但无论是否超时,从SocketInputStream读取有时会导致-1 ( Eg when multiple clients simultaneously connect to the same host:port , then even though the devices seem connected, the result of a read could immediately restult in a -1 (never returning data). ) 例如,当多个客户端同时连接到同一个host:port时,即使设备看起来已连接, read的结果也可能立即返回-1 (从不返回数据)。

  • Serialio communication will always return -1 ; Serialio通信将始终返回-1 You can also set a timeout (use setTimeoutRx ), the read will first block for x ms, but the result will still be -1 if nothing's found.您还可以设置超时(使用setTimeoutRx ), read将首先阻塞 x 毫秒,但如果未找到任何内容,结果仍将为-1 (Remark: but there are multiple serial io classes available, behaviour could be vendor dependent.) (备注:但有多个串行 io 类可用,行为可能取决于供应商。)

  • Files (readers or streams) will result in an EOFException .文件(阅读器或流)将导致EOFException

Work to a Generic Solution:为通用解决方案工作:

  • If you wrap any of the above streams in a DataInputStream , then you can use methods like readByte , readChar , etc. All -1 values are converted to EOFException .如果将上述任何流包装在DataInputStream中,则可以使用readBytereadChar等方法。所有-1值都将转换为EOFException (PS: If you perform a lot of small reads, then it's a good idea to wrap it in a BufferedInputStream first) (PS:如果你执行了大量的小读取,那么最好先将它包装在BufferedInputStream中)
  • Both SocketTimeoutException and EOFException extend IOException , and there are several other possible IOException 's. SocketTimeoutExceptionEOFException都扩展了IOException ,还有其他几个可能的IOException It is convenient to just check for IOException 's to detect communication issues.只检查IOException来检测通信问题很方便。

Another sensitive topic is flushing.另一个敏感话题是潮红。 flush in terms of sockets means "send it now", but in terms of Serialio it means "discard the buffer".就 sockets 而言, flush表示“立即发送”,但就 Serialio 而言,它表示“丢弃缓冲区”。

I think You can receive the entire data stream if you use thread.sleep()我想如果你使用 thread.sleep() 你可以收到整个数据 stream

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM