타임아웃으로 Input Stream에서 읽을 수 있습니까?
구체적으로는 다음과 같은 방법을 쓰는 것이 문제입니다.
int maybeRead(InputStream in, long timeout)
여기서 return 값은 'syslog' 밀리초 이내에 데이터를 사용할 수 있는 경우 in.read()와 같고, 그렇지 않은 경우 -2입니다.메서드가 반환되기 전에 생성된 스레드가 모두 종료되어야 합니다.
논쟁을 피하기 위해 제목은 java.io 입니다.Sun에서 문서화된 InputStream(자바 버전).이것은 보기만큼 간단하지 않습니다.다음은 Sun의 문서에 의해 직접적으로 뒷받침되는 몇 가지 사실이다.
in.read() 메서드는 중단되지 않을 수 있습니다.
InputStream을 Reader 또는 InterruptibleChannel로 래핑하는 것은 도움이 되지 않습니다.이러한 클래스는 InputStream의 호출 메서드밖에 사용할 수 없기 때문입니다.이러한 클래스를 사용할 수 있다면 동일한 로직을 InputStream에 직접 실행하는 솔루션을 작성할 수 있습니다.
in.available()은 항상 0을 반환할 수 있습니다.
in.close() 메서드는 차단하거나 아무것도 하지 않을 수 있습니다.
다른 실을 끊을 수 있는 일반적인 방법은 없습니다.
inputStream.available() 사용
System.in.available()이 0을 반환하는 것은 항상 허용됩니다.
그 반대인 것을 발견했습니다.사용 가능한 바이트 수에 대해 항상 최적인 값이 반환됩니다. for 바 jav :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을 반환하면 위의 조건에 실패합니다.
첫 번째 경고:InputStream의 구체적인 서브클래스는 available()을 담당합니다.
InputStream
상상클클 클다다다다다데이터 소스가 없습니다.이용 가능한 데이터가 있는 것은 의미가 없습니다."javadoc"의 available()
을 사용하다
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
실제로 콘크리트 입력 스트림 클래스는 사용 가능한()보다 우선하며 상수 0이 아닌 의미 있는 값을 제공합니다.
두 번째 경고:Windows 로 입력을 입력할 때는, 캐리지 리턴을 사용해 주세요.
「 」를 하고 있는 System.in
명령 셸이 명령을 전달했을 때만 프로그램이 입력을 받습니다.somefile > java myJava App some command | java myJava App ) 。windows exe 쉘 내에서 .Windows cmd.exe 의 cmd.exe 의 경우 cmd.exe 의 경우.control-m 또는 control-m)에 됩니다.<enter>
이치노InputStream.available()은 0으로 설정합니다.이것은 올바른 동작입니다.그 시점에는 사용 가능한 데이터가 없습니다.셸에서 데이터를 사용할 수 있게 되면 메서드는 0보다 큰 값을 반환합니다.NB: Cygwin cmd.exe입니다.
가장 심플한 솔루션(차단 불필요, 타임아웃 불필요)
이것만 사용해 주세요.
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.
되지 않은 " "를 사용할 수 ")Socket.setSoTimeout()
은 '를
다음과 같은 실행자와 스트림이 있다고 가정합니다.
ExecutorService executor = Executors.newFixedThreadPool(2);
final PipedOutputStream outputStream = new PipedOutputStream();
final PipedInputStream inputStream = new PipedInputStream(outputStream);
데이터를 쓴 다음 5초 동안 기다렸다가 마지막 데이터를 쓰고 스트림을 닫는 라이터가 있습니다.
Runnable writeTask = new Runnable() {
@Override
public void run() {
try {
outputStream.write(1);
outputStream.write(2);
Thread.sleep(5000);
outputStream.write(3);
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
};
executor.submit(writeTask);
이것을 읽는 일반적인 방법은 다음과 같습니다.판독은 데이터에 대해 무기한 차단되므로 5초 후에 완료됩니다.
long start = currentTimeMillis();
int readByte = 1;
// Read data without timeout
while (readByte >= 0) {
readByte = inputStream.read();
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
System.out.println("Complete in " + (currentTimeMillis() - start) + "ms");
출력:
Read: 1
Read: 2
Read: 3
Complete in 5001ms
만약 글쓴이가 응답하지 않는 것과 같은 보다 근본적인 문제가 있다면 독자는 영원히 차단할 것이다.나중에 읽기를 랩하면 다음과 같이 타임아웃을 제어할 수 있습니다.
int readByte = 1;
// Read data with timeout
Callable<Integer> readTask = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return inputStream.read();
}
};
while (readByte >= 0) {
Future<Integer> future = executor.submit(readTask);
readByte = future.get(1000, TimeUnit.MILLISECONDS);
if (readByte >= 0)
System.out.println("Read: " + readByte);
}
출력:
Read: 1
Read: 2
Exception in thread "main" java.util.concurrent.TimeoutException
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228)
at java.util.concurrent.FutureTask.get(FutureTask.java:91)
at test.InputStreamWithTimeoutTest.main(InputStreamWithTimeoutTest.java:74)
타임아웃을 잡을 수 있다예외로 하고 내가 하고 싶은 대로 해.
InputStream이 소켓에서 지원되는 경우 setSoTimeout을 사용하여 소켓 타임아웃(밀리초)을 설정할 수 있습니다.read() 콜이 지정된 타임아웃 시간 내에 차단 해제되지 않으면 Socket Timeout이 느려집니다.예외.
read() 호출을 하기 전에 소켓에서 setSoTimeout을 호출해야 합니다.
무작정 받아들이기보다는 문제점에 대해 의문을 제기하고 싶다.필요한 것은 콘솔 또는 네트워크를 통한 타임아웃뿐입니다.는, 「」가 있습니다.Socket.setSoTimeout()
★★★★★★★★★★★★★★★★★」HttpURLConnection.setReadTimeout()
둘 다 필요한 기능을 수행합니다.단, 구성/수정 시 올바르게 설정하기만 하면 됩니다.InputStream이 설계 불량으로 인해 구현이 매우 어려워지는 경우 나중에 응용 프로그램의 임의의 포인트에 맡겨야 합니다.
Java NIO 패키지의 클래스를 사용한 적은 없지만, 여기에서 도움이 될 수 있을 것 같습니다.구체적으로는 java.nio.channels.채널과 java.nio.channels.인터럽트 가능 채널
다음은 System.in에서 NIO File Channel을 가져와 타임아웃을 사용하여 데이터의 가용성을 체크하는 방법입니다.이것은 질문에 기재되어 있는 문제의 특수한 경우입니다.콘솔에서 실행하고 입력을 입력하지 않고 결과를 기다립니다.Windows 및 Linux의 Java 6에서 성공적으로 테스트되었습니다.
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedByInterruptException;
public class Main {
static final ByteBuffer buf = ByteBuffer.allocate(4096);
public static void main(String[] args) {
long timeout = 1000 * 5;
try {
InputStream in = extract(System.in);
if (! (in instanceof FileInputStream))
throw new RuntimeException(
"Could not extract a FileInputStream from STDIN.");
try {
int ret = maybeAvailable((FileInputStream)in, timeout);
System.out.println(
Integer.toString(ret) + " bytes were read.");
} finally {
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/* unravels all layers of FilterInputStream wrappers to get to the
* core InputStream
*/
public static InputStream extract(InputStream in)
throws NoSuchFieldException, IllegalAccessException {
Field f = FilterInputStream.class.getDeclaredField("in");
f.setAccessible(true);
while( in instanceof FilterInputStream )
in = (InputStream)f.get((FilterInputStream)in);
return in;
}
/* Returns the number of bytes which could be read from the stream,
* timing out after the specified number of milliseconds.
* Returns 0 on timeout (because no bytes could be read)
* and -1 for end of stream.
*/
public static int maybeAvailable(final FileInputStream in, long timeout)
throws IOException, InterruptedException {
final int[] dataReady = {0};
final IOException[] maybeException = {null};
final Thread reader = new Thread() {
public void run() {
try {
dataReady[0] = in.getChannel().read(buf);
} catch (ClosedByInterruptException e) {
System.err.println("Reader interrupted.");
} catch (IOException e) {
maybeException[0] = e;
}
}
};
Thread interruptor = new Thread() {
public void run() {
reader.interrupt();
}
};
reader.start();
for(;;) {
reader.join(timeout);
if (!reader.isAlive())
break;
interruptor.start();
interruptor.join(1000);
reader.join(1000);
if (!reader.isAlive())
break;
System.err.println("We're hung");
System.exit(1);
}
if ( maybeException[0] != null )
throw maybeException[0];
return dataReady[0];
}
}
흥미롭게도 콘솔이 아닌 NetBeans 6.5 내에서 프로그램을 실행하면 타임아웃이 전혀 작동하지 않으며 좀비 스레드를 종료하려면 System.exit()에 대한 호출이 실제로 필요합니다.인터럽터 스레드가 리더에 대한 콜을 차단합니다(!).인터럽트다른 테스트 프로그램(여기에는 표시되지 않음)이 추가로 채널을 닫으려고 하지만 그것도 작동하지 않습니다.
jt가 말했듯이 NIO는 최적의(올바른) 솔루션입니다.그러나 InputStream을 사용할 수 없는 경우에는 다음 중 하나를 수행합니다.
InputStream에서 읽기 전용 작업을 하는 스레드를 생성하여 결과를 차단 없이 원래 스레드에서 읽을 수 있는 버퍼에 넣습니다.스트림의 인스턴스가 1개밖에 없는 경우 이 방법은 잘 작동합니다.그렇지 않으면 스레드 클래스에서 사용되지 않는 메서드를 사용하여 스레드를 종료할 수 있지만, 이로 인해 리소스 누수가 발생할 수 있습니다.
IsAvailable에 의존하여 블록 없이 읽을 수 있는 데이터를 나타냅니다.그러나 경우에 따라서는 (Sockets 등) isAvailable이 0 이외의 것을 보고하기 위해 잠재적인 차단 판독이 필요할 수 있습니다.
이 답변에서 영감을 얻어 저는 좀 더 객체 지향적인 솔루션을 생각해냈습니다.
이것은 문자를 읽으려는 경우에만 유효합니다.
BufferedReader를 덮어쓰고 다음과 같은 기능을 구현할 수 있습니다.
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
( . . . )
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
break; // Should restore flag
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("Read timed out");
}
}
여기 거의 완벽한 예가 있습니다.
일부 메서드에서 0을 반환하고 있으므로 필요에 따라 -2로 변경해야 합니다만, BufferedReader 계약에서는 0이 더 적합하다고 생각합니다.아무 문제 없이 0글자만 읽혔어요.readLine 메서드는 퍼포먼스 킬러입니다.readLine을 실제로 사용하려면 완전히 새로운 BufferedReader를 생성해야 합니다.지금은 스레드 세이프가 아닙니다.readLines가 회선을 기다리는 동안 누군가가 작업을 호출하면 예기치 않은 결과가 발생합니다.
난 내가 있는 곳에 -2를 되돌리는 걸 좋아하지 않아.EOS를 고려하기 위해 int < 0을 체크하는 사람도 있기 때문에 예외를 둘 수 있습니다.어쨌든, 이러한 메서드는 「차단할 수 없다」라고 주장하고 있기 때문에, 그 스테이트먼트가 실제로 사실인지 아닌지를 확인해, 덮어쓰지 말아 주세요.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
/**
*
* readLine
*
* @author Dario
*
*/
public class SafeBufferedReader extends BufferedReader{
private long millisTimeout;
private long millisInterval = 100;
private int lookAheadLine;
public SafeBufferedReader(Reader in, int sz, long millisTimeout) {
super(in, sz);
this.millisTimeout = millisTimeout;
}
public SafeBufferedReader(Reader in, long millisTimeout) {
super(in);
this.millisTimeout = millisTimeout;
}
/**
* This is probably going to kill readLine performance. You should study BufferedReader and completly override the method.
*
* It should mark the position, then perform its normal operation in a nonblocking way, and if it reaches the timeout then reset position and throw IllegalThreadStateException
*
*/
@Override
public String readLine() throws IOException {
try {
waitReadyLine();
} catch(IllegalThreadStateException e) {
//return null; //Null usually means EOS here, so we can't.
throw e;
}
return super.readLine();
}
@Override
public int read() throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read();
}
@Override
public int read(char[] cbuf) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return -2; // I'd throw a runtime here, as some people may just be checking if int < 0 to consider EOS
}
return super.read(cbuf);
}
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(cbuf, off, len);
}
@Override
public int read(CharBuffer target) throws IOException {
try {
waitReady();
} catch(IllegalThreadStateException e) {
return 0;
}
return super.read(target);
}
@Override
public void mark(int readAheadLimit) throws IOException {
super.mark(readAheadLimit);
}
@Override
public Stream<String> lines() {
return super.lines();
}
@Override
public void reset() throws IOException {
super.reset();
}
@Override
public long skip(long n) throws IOException {
return super.skip(n);
}
public long getMillisTimeout() {
return millisTimeout;
}
public void setMillisTimeout(long millisTimeout) {
this.millisTimeout = millisTimeout;
}
public void setTimeout(long timeout, TimeUnit unit) {
this.millisTimeout = TimeUnit.MILLISECONDS.convert(timeout, unit);
}
public long getMillisInterval() {
return millisInterval;
}
public void setMillisInterval(long millisInterval) {
this.millisInterval = millisInterval;
}
public void setInterval(long time, TimeUnit unit) {
this.millisInterval = TimeUnit.MILLISECONDS.convert(time, unit);
}
/**
* This is actually forcing us to read the buffer twice in order to determine a line is actually ready.
*
* @throws IllegalThreadStateException
* @throws IOException
*/
protected void waitReadyLine() throws IllegalThreadStateException, IOException {
long timeout = System.currentTimeMillis() + millisTimeout;
waitReady();
super.mark(lookAheadLine);
try {
while(System.currentTimeMillis() < timeout) {
while(ready()) {
int charInt = super.read();
if(charInt==-1) return; // EOS reached
char character = (char) charInt;
if(character == '\n' || character == '\r' ) return;
}
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
} finally {
super.reset();
}
throw new IllegalThreadStateException("readLine timed out");
}
protected void waitReady() throws IllegalThreadStateException, IOException {
if(ready()) return;
long timeout = System.currentTimeMillis() + millisTimeout;
while(System.currentTimeMillis() < timeout) {
if(ready()) return;
try {
Thread.sleep(millisInterval);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
if(ready()) return; // Just in case.
throw new IllegalThreadStateException("read timed out");
}
}
언급URL : https://stackoverflow.com/questions/804951/is-it-possible-to-read-from-a-inputstream-with-a-timeout
'programing' 카테고리의 다른 글
Vue : 텍스트 영역 입력의 문자를 제한하시겠습니까?필터를 잘라내시겠습니까? (0) | 2022.06.01 |
---|---|
Vue 컴포넌트 템플릿의 요소 속성에 문자열과 변수를 조합 (0) | 2022.06.01 |
Vue 라우터 - 매개 변수가 전달되지 않음 (0) | 2022.05.31 |
Nuxt.js의 서버 측과 Vuex 스토어 동기화 (0) | 2022.05.31 |
vue 구성 요소에서 setInterval을 사용하는 방법 (0) | 2022.05.31 |