Java InputStream 阻塞读取

发布于 2024-07-15 02:01:30 字数 1514 浏览 4 评论 0 原文

根据 Java api 文档 InputStream 描述为:

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

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

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

或者,只有当有另一个线程访问流并且您的 read() 无法访问流时,read() 才会阻塞吗?


这引出了我的下一个问题。 我曾经有一个事件监听器(由我的库提供),当数据可用时它会通知我。 当我收到通知时,我将启动 while((aByte = read()) > -1) 循环来存储字节。 当我在很短的时间内收到两个事件并且并未显示所有数据时,我感到很困惑。 似乎只显示了第二个事件数据的尾部,其余的都缺失了。

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

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

According to the Java api documentation, the read() method that is declared in InputStream is described as:

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.

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

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?

Or does read() only block if there's another thread accessing the stream and your read() cannot access the 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 start while((aByte = read()) > -1) cycle to store the byte. I was puzzled when I'd get TWO events in very short time 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.

I eventually changed my code so that when I get an event I'd started if(inputStream.available() > 0) while((aByte = read()) > -1) to store the byte. Now it worked properly and all my data was displayed.

Can someone explain this behavior? The available() method of InputStream 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

心欲静而疯不止 2024-07-22 02:01:30

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

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

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


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

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. Until this signal is received, read operations on such a stream can block.

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

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.


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.

悲欢浪云 2024-07-22 02:01:30

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

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

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.

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.

起风了 2024-07-22 02:01:30

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

其次,如果您可以从 InputStream 读取数据,直到获得 -1,然后等待并可以稍后再次读取,那么您正在使用的 InputStream 实现就已损坏! InputStream 的契约明确规定,当没有更多数据可供读取时,InputStream.read() 只应返回 -1,因为已到达流的末尾,并且不再有任何数据可用 - 就像当您从文件中读取数据并到达末尾(1)时一样。

“现在没有更多数据可用,请稍候,您将获得更多”的行为是 read() 阻塞,直到有可用数据(或抛出异常)为止才返回。

  1. 正如erickson(目前排名靠前)的答案的讨论中所指出的那样,FileInputStream实现实际上可以读取“文件结尾”之后的内容,并在 read() 返回 -1 后提供更多数据 - 如果稍后将数据添加到文件中。 这是一种边缘情况,基本上是常见 InputStream 实现中唯一的这种情况(或者在最坏的情况下 - 非常非常罕见)。 如果您知道您使用 FileInputStream 并且期望您读取的文件添加了其他数据(常见的示例是跟踪日志文件),则应该考虑到这一点,但否则这只是 InputStream API 的缺陷,无论如何 - 如果您可以停止使用 java.io 风格的阻塞 IO API 并使用 < code>java.nio 非阻塞 IO API。

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.

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

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

  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. This is an edge case and is basically the only such case in common InputStream implementations (or at worst - very very rare). 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.
笔落惊风雨 2024-07-22 02:01:30

默认情况下,提供的 RXTX 输入流的行为不兼容。

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

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

来源:RXTX 串行连接 - 阻塞 read() 问题

By default the behavior of the provided RXTX InputStream is not compliant.

You have to set the receive threshold to 1 and disable the receive timeout:

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

Source: RXTX serial connection - issue with blocking read()

孤芳又自赏 2024-07-22 02:01:30

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

现在,如果他们所说的是正确的,即:Serial 应该在 -1 上关闭,那么为什么我们必须注意 OnCTS、pmCarroerDetect、onDSR、onRingIndicator 等内容......
哎呀,如果 0 表示它在那里,而 -1 表示它不存在,那么就搞砸所有这些检测功能! :-)

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

现在,讨论具体细节:

问:“似乎只有第二个事件数据的尾部会显示,其余部分都丢失了。”

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

问:“我最终更改了代码,以便当我收到事件时,我调用 if(inputStream.available() > 0) while((aByte = read()) > -1) 存储字节”

A:太棒了......这就是那里的好东西。 现在,您的数据缓冲区位于 IF 语句内,您的第二条消息不会破坏您的第一条消息......好吧,实际上,它可能只是第一条消息。 但现在,您将一次性读取所有内容,并保持数据完整。

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

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

  • 我得到了一些 TCP/IP 数据,比如说 20 个字节。
  • 所以我收到了接收数据的 OnEvent。
  • 我什至从 20 个字节开始“读取”。
  • 在我读完 20 个字节之前...我又得到了 10 个字节。
  • 然而 TCP/IP 看起来要通知我,哦,看到标志仍然是 SET,并且不会再通知我了。
  • 然而,我读完了我的 20 个字节(available() 说有 20 个)...
  • ...最后 10 个字节保留在 TCP/IP Q 中...因为我没有收到通知。

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

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

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

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.

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

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.

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

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.

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

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.

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

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.

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.

夜灵血窟げ 2024-07-22 02:01:30

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

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

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

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

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

  • 文件(读取器或流)将导致 <代码>EOFException

制定通用解决方案:

  • 如果将上述任何流包装在 DataInputStream 中,则可以使用 readByte< 等方法/code>、readChar 等。 所有 -1 值都会转换为 EOFException (PS:如果您执行大量小读取,那么这是一个好主意首先将其包装在 BufferedInputStream 中)
  • SocketTimeoutExceptionEOFException 都扩展了 IOException ,并且有其他几个可能的IOException只需检查 IOException 即可方便地检测通信问题。

另一个敏感主题是刷新。 flush 在套接字方面意味着“立即发送”,但在 Serialio 方面则意味着“丢弃缓冲区”。

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

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 read will block for x ms. If still nothing is received then a SocketTimeoutException will be thrown.

    But with or without timeout, reading from a SocketInputStream can 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 read could immediately restult in a -1 (never returning data).)

  • Serialio communication will always return -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. (Remark: but there are multiple serial io classes available, behaviour could be vendor dependent.)

  • Files (readers or streams) will result in an 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. (PS: If you perform a lot of small reads, then it's a good idea to wrap it in a BufferedInputStream first)
  • Both SocketTimeoutException and EOFException extend IOException , and there are several other possible IOException's. It is convenient to just check for IOException's to detect communication issues.

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

冬天旳寂寞 2024-07-22 02:01:30

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文