是否可以从带有超时的 InputStream 中读取? [英] Is it possible to read from a InputStream with a timeout?

查看:40
本文介绍了是否可以从带有超时的 InputStream 中读取?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

具体来说,问题是写一个这样的方法:

Specifically, the problem is to write a method like this:

int maybeRead(InputStream in, long timeout)

如果数据在 'timeout' 毫秒内可用,则返回值与 in.read() 相同,否则为 -2.在方法返回之前,任何产生的线程都必须退出.

where the return value is the same as in.read() if data is available within 'timeout' milliseconds, and -2 otherwise. Before the method returns, any spawned threads must exit.

为了避免争论,这里的主题是 java.io.InputStream,正如 Sun(任何 Java 版本)所记录的那样.请注意,这并不像看起来那么简单.以下是 Sun 文档直接支持的一些事实.

To avoid arguments, the subject here java.io.InputStream, as documented by Sun (any Java version). Please note this is not as simple as it looks. Below are some facts which are supported directly by Sun's documentation.

  1. in.read() 方法可能是不可中断的.

  1. The in.read() method may be non-interruptible.

将 InputStream 包装在 Reader 或 InterruptibleChannel 中无济于事,因为所有这些类都可以调用 InputStream 的方法.如果可以使用这些类,就可以编写一个直接在 InputStream 上执行相同逻辑的解决方案.

Wrapping the InputStream in a Reader or InterruptibleChannel doesn't help, because all those classes can do is call methods of the InputStream. If it were possible to use those classes, it would be possible to write a solution that just executes the same logic directly on the InputStream.

in.available() 返回 0 总是可以接受的.

It is always acceptable for in.available() to return 0.

in.close() 方法可能会阻塞或什么都不做.

The in.close() method may block or do nothing.

没有通用的方法可以杀死另一个线程.

There is no general way to kill another thread.

推荐答案

使用 inputStream.available()

System.in.available() 返回 0 总是可以接受的.

我发现了相反的情况——它总是返回可用字节数的最佳值.InputStream.available() 的 Javadoc:

I've found the opposite - it always returns the best value for the number of bytes available. Javadoc for InputStream.available():

Returns an estimate of the number of bytes that can be read (or skipped over) 
from this input stream without blocking by the next invocation of a method for 
this input stream.

由于时间/过时,估计是不可避免的.这个数字可能是一次性的低估,因为新数据不断到达.然而,它总是在下一次调用时赶上"——它应该考虑所有到达的数据,除非在新调用的那一刻到达.当有数据不符合上述条件时永久返回0.

An estimate is unavoidable due to timing/staleness. The figure can be a one-off underestimate because new data are constantly arriving. However it always "catches up" on the next call - it should account for all arrived data, bar that arriving just at the moment of the new call. Permanently returning 0 when there are data fails the condition above.

第一个警告:InputStream 的具体子类负责 available()

InputStream 是一个抽象类.它没有数据源.拥有可用数据是没有意义的.因此,available() 的 javadoc 也指出:

InputStream is an abstract class. It has no data source. It's meaningless for it to have available data. Hence, javadoc for available() also states:

The available method for class InputStream always returns 0.

This method should be overridden by subclasses.

实际上,具体的输入流类确实覆盖了 available(),提供了有意义的值,而不是常量 0.

And indeed, the concrete input stream classes do override available(), providing meaningful values, not constant 0s.

第二个警告:确保在 Windows 中输入时使用回车符.

如果使用 System.in,您的程序仅在您的命令外壳程序移交时接收输入.如果您使用文件重定向/管道(例如 somefile > java myJavaApp 或 somecommand | java myJavaApp ),则输入数据通常会立即移交.但是,如果您手动输入,则数据切换可能会延迟.例如.使用 windows cmd.exe shell,数据在 cmd.exe shell 中缓冲.数据仅在回车(control-m 或 )后传递给正在执行的 java 程序.这是执行环境的限制.当然,只要 shell 缓冲数据, InputStream.available() 就会返回 0 - 这是正确的行为;那时没有可用的数据.一旦从 shell 获得数据,该方法就会返回一个 > 0 的值.注意:Cygwin 也使用 cmd.exe.

If using System.in, your program only receives input when your command shell hands it over. If you're using file redirection/pipes (e.g. somefile > java myJavaApp or somecommand | java myJavaApp ), then input data are usually handed over immediately. However, if you manually type input, then data handover can be delayed. E.g. With windows cmd.exe shell, the data are buffered within cmd.exe shell. Data are only passed to the executing java program following carriage-return (control-m or <enter>). That's a limitation of the execution environment. Of course, InputStream.available() will return 0 for as long as the shell buffers the data - that's correct behaviour; there are no available data at that point. As soon as the data are available from the shell, the method returns a value > 0. NB: Cygwin uses cmd.exe too.

就用这个:

    byte[] inputData = new byte[1024];
    int result = is.read(inputData, 0, is.available());  
    // result will indicate number of bytes read; -1 for EOF with no data read.

或等价地,

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in, Charset.forName("ISO-8859-1")),1024);
    // ...
         // inside some iteration / processing logic:
         if (br.ready()) {
             int readCount = br.read(inputData, bufferOffset, inputData.length-bufferOffset);
         }

更丰富的解决方案(在超时时间内最大程度地填充缓冲区)

声明:

public static int readInputStreamWithTimeout(InputStream is, byte[] b, int timeoutMillis)
     throws IOException  {
     int bufferOffset = 0;
     long maxTimeMillis = System.currentTimeMillis() + timeoutMillis;
     while (System.currentTimeMillis() < maxTimeMillis && bufferOffset < b.length) {
         int readLength = java.lang.Math.min(is.available(),b.length-bufferOffset);
         // can alternatively use bufferedReader, guarded by isReady():
         int readResult = is.read(b, bufferOffset, readLength);
         if (readResult == -1) break;
         bufferOffset += readResult;
     }
     return bufferOffset;
 }

然后使用这个:

    byte[] inputData = new byte[1024];
    int readCount = readInputStreamWithTimeout(System.in, inputData, 6000);  // 6 second timeout
    // readCount will indicate number of bytes read; -1 for EOF with no data read.

这篇关于是否可以从带有超时的 InputStream 中读取?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆