Java InputStream 阻塞读取

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/611760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:52:02  来源:igfitidea点击:

Java InputStream blocking read

javablockinginputstreamrxtxjava-io

提问by jbu

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. This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.

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

I have a while(true)loop doing a read and I always get -1 when nothing's sent over the stream. That's expected.

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

My question is when would read() ever block? Since if it doesn't get any data it returns -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?

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

Or does read() only block if there's another thread accessing the stream and your read() cannot access the stream?

或者 read() 只有在有另一个线程访问流而您的 read() 无法访问流时才会阻塞?



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. 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.

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

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. Now it worked properly and all my data was displayed.

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

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?). 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. Why would doing this cause not all of my data to be displayed?

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

回答by Vladimir Dyuzhev

It returns -1 if it's end of stream. If stream is still open (i.e. socket connection) but no data has reached the reading side (server is slow, networks is slow,...) the read() blocks.

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

You don't need call available(). I have a hard time understanding your notification design, but you don't need any calls except read() itself. Method available() is there for convenience only.

你不需要 call available()。我很难理解您的通知设计,但除了 read() 本身之外,您不需要任何调用。方法 available() 只是为了方便。

回答by erickson

The underlying data source for some implementations of InputStreamcan signal that the end of the stream has been reached, and no more data will be sent. Until this signal is received, read operations on such a stream can block.

某些实现的底层数据源InputStream可以表示已到达流的末尾,并且不会再发送数据。在收到此信号之前,对此类流的读取操作可能会阻塞。

For example, an InputStreamfrom a Socketsocket will block, rather than returning EOF, until a TCP packet with the FIN flag set is received. 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. (If a blocking read results in an exception, on the other hand, some data may have been lost.)

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

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. In the absence of such a format or protocol, however, you can't be sure when the other side is done sending data.

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



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.

关于您的第二个问题,听起来您可能遇到了竞争状况。没有看到有问题的代码,我猜测问题实际上在于您的“显示”方法。也许通过第二个通知显示的尝试以某种方式破坏了在第一个通知期间完成的工作。

回答by Guss

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. 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. If you plan to use a single IO stream in more then one thread, always synchronized() {}on some external constraint.

好吧,这有点乱,所以首先让我们澄清一下:InputStream.read()阻塞与多线程无关。如果您有多个线程从同一个输入流中读取数据,并且触发了两个彼此非常接近的事件——其中每个线程都试图使用一个事件,那么您就会受到破坏:要读取的第一个线程将获得一些字节(可能是全部)字节),当第二个线程被调度时,它将读取其余的字节。如果您计划在多个线程中使用单个 IO 流,则始终synchronized() {}处于某些外部约束条件下。

Second, if you can read from your InputStreamuntil you get -1 and then wait and can read again later, then the InputStream implementation you are using is broken! The contract for InputStreamclearly states that an InputStream.read()should only return -1 when there is no more data to read because the end of the entire stream has been reached and no more data will EVER be available - like when you read from a file and you reach the end.

其次,如果您可以读取InputStream直到获得 -1,然后等待并稍后再次读取,那么您正在使用的 InputStream 实现已损坏!合同InputStream明确规定,InputStream.read()当没有更多数据要读取时,应该只返回 -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()阻塞,直到有一些数据可用(或抛出异常)才返回。

回答by Guss

Aye! Don't give up on your stream yet Jbu. We are talking Serial communication here. For serial stuff, it is absolutely expected that a -1 can/will be returned on reads, 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, -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, or (in most cases) no hardware flow control. But the link is physical, and still connected by "copper" and still perfectly live.

是啊!不要放弃你的直播 Jbu。我们在这里谈论串行通信。对于串行的东西,绝对期望在读取时可以/将返回 -1,但仍然期待稍后的数据。问题是大多数人都习惯于处理 TCP/IP,除非 TCP/IP 断开连接,否则它应该总是返回 0...然后是的,-1 是有道理的。但是,对于串行,长时间没有数据流,也没有“HTTP Keep Alive”或 TCP/IP 心跳,或者(在大多数情况下)没有硬件流控制。但是链接是物理的,并且仍然通过“铜”连接并且仍然完美地存在。

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! :-)

现在,如果他们说的是正确的,即:Serial 应该在 -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."

问:“似乎只显示第二个事件数据的尾部,其余的都不见了。”

A: I'm going to guess that you were in a loop, re-using the same byte[] buffer. 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.

答:我猜你是在循环中,重新使用相同的 byte[] 缓冲区。第一条消息传入,尚未显示在屏幕/日志/标准输出上(因为您处于循环中),然后您读取第二条消息,替换缓冲区中的第一条消息数据。同样,因为我会猜测您不会存储您读取的数量,然后确保将您的存储缓冲区偏移前一个读取数量。

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. 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. But now, you will read it all in one shot, keeping the data intact.

A:太棒了……那就是那里的好东西。现在,您的数据缓冲区位于 IF 语句中,您的第二条消息不会破坏您的第一条……嗯,实际上,它可能只是第一条大(呃)消息。但是现在,您将一口气读完所有内容,同时保持数据完整。

C: "... race condition ..."

C:“……比赛条件……”

A: Ahhh, the good ol' catch all scape goat! 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. 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? 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.

A:啊哈,好人把所有的山羊都抓了!竞争条件... :-) 是的,这可能是一个竞争条件,事实上它很可能是。但是,它也可能只是 RXTX 清除标志的方式。“数据可用标志”的清除可能不会像人们预期的那样快。例如,有人知道 read VS readLine 与清除先前存储数据的缓冲区和重新设置事件标志之间的区别吗?我也没有。:-) 我也找不到答案......但是......让我再啰嗦几句。事件驱动编程仍然存在一些缺陷。让我给你举一个我最近不得不处理的真实世界的例子。

  • I got some TCP/IP data, lets say, 20 bytes.
  • So I receive the OnEvent for Received Data.
  • I start my 'read' even on the 20 bytes.
  • Before I finish reading my 20 bytes... I get another 10 bytes.
  • The TCP/IP however, looks to notify me, oh, sees that the flag is still SET, and will not notify me again.
  • However, I finish reading my 20 bytes (available() said there were 20)...
  • ... and the last 10 bytes remain in the TCP/IP Q... because I was not notified of them.
  • 我得到了一些 TCP/IP 数据,比如说 20 个字节。
  • 所以我收到 OnEvent for Received Data。
  • 我什至在 20 个字节上开始我的“阅读”。
  • 在我读完 20 个字节之前……我又得到了 10 个字节。
  • 然而TCP/IP,看起来通知我,哦,看到标志仍然是SET,不会再通知我。
  • 但是,我读完了我的 20 个字节(available() 说有 20 个)...
  • ...最后 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. 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. If available() returns 0 (or -1), then relax... sit back... and wait for the next OnEvent notification.

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

回答by manju

I think You can receive the entire data stream if you use thread.sleep()

我认为如果您使用 thread.sleep(),您可以接收整个数据流

回答by Robert

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() 的问题

回答by bvdb

InputStreamis just an abstract class, unfortunately the implementation decides what happens.

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

What happens if nothing is found:

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

  • Sockets(i.e. SocketInputStream) will block until data is received (by default). But it's possible to set a timeout (see: setSoTimeout), then the readwill block for x ms. If still nothing is received then a SocketTimeoutExceptionwill be thrown.

    But with or without timeout, reading from a SocketInputStreamcan sometimes result in a -1.(E.g. when multiple clients simultaneously connect to the same host:port, then even though the devices seem connected, the result of a readcould immediately restult in a -1(never returning data).)

  • Serialiocommunication will always return -1; You can also set a timeout (use setTimeoutRx), the readwill first block for x ms, but the result will still be -1if nothing's found. (Remark: but there are multiple serial io classes available, behaviour could be vendor dependent.)

  • Files(readers or streams) will result in an EOFException.

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

    但是无论是否超时,从 a 读取SocketInputStream有时会导致-1. 例如,当多个客户端同时连接到同一个 时host:port,即使设备看起来已连接,a 的结果read也可能立即产生 a -1(从不返回数据)。

  • Serialio通讯总会返回-1;您还可以设置超时(使用setTimeoutRx),read将首先阻塞 x 毫秒,但-1如果没有找到任何结果,结果仍然是。(备注:但有多个串行 io 类可用,行为可能取决于供应商。)

  • 文件(读取器或流)将导致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 -1values are converted to EOFException.(PS: If you perform a lot of small reads, then it's a good idea to wrap it in a BufferedInputStreamfirst)
  • Both SocketTimeoutExceptionand EOFExceptionextend IOException, and there are several other possible IOException's. It is convenient to just check for IOException's to detect communication issues.
  • 如果包装任何上述流的DataInputStream,那么你可以使用类似的方法readBytereadChar等等。所有-1值都转换为EOFException. (PS:如果你执行大量的小读,那么最好BufferedInputStream先把它包装起来)
  • 无论SocketTimeoutExceptionEOFException扩展IOException,以及还有其他一些可能IOException的。只需检查IOException's 即可方便地检测通信问题。

Another sensitive topic is flushing. flushin terms of sockets means "send it now", but in terms of Serialio it means "discard the buffer".

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