Korzystanie z metody inputStream.available ()
Zawsze akceptowalne jest, aby System.in.available () zwracało 0.
Znalazłem coś przeciwnego - zawsze zwraca najlepszą wartość dla liczby dostępnych bajtów. Javadoc dla 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.
Oszacowanie jest nieuniknione ze względu na harmonogram / nieaktualność. Liczba ta może być jednorazowym niedoszacowaniem, ponieważ stale napływają nowe dane. Jednak zawsze „nadrabia zaległości” przy następnym wywołaniu - powinien uwzględniać wszystkie otrzymane dane, z wyjątkiem tego, że przychodzi tylko w momencie nowego połączenia. Trwałe zwracanie 0, gdy istnieją dane, nie spełnia powyższego warunku.
Pierwsze zastrzeżenie: konkretne podklasy InputStream są odpowiedzialne za available ()
InputStream
jest klasą abstrakcyjną. Nie ma źródła danych. Posiadanie dostępnych danych nie ma sensu. Dlatego javadoc dla available()
również stwierdza:
The available method for class InputStream always returns 0.
This method should be overridden by subclasses.
I rzeczywiście, konkretne klasy strumienia wejściowego przesłaniają available (), dostarczając znaczące wartości, a nie stałe 0.
Drugie zastrzeżenie: upewnij się, że używasz powrotu karetki podczas wpisywania danych wejściowych w systemie Windows.
Jeśli używasz System.in
, twój program otrzymuje dane wejściowe tylko wtedy, gdy powłoka poleceń przekazuje je. Jeśli używasz przekierowań plików / potoków (np. Somefile> java myJavaApp lub somecommand | java myJavaApp), dane wejściowe są zwykle przekazywane natychmiast. Jeśli jednak ręcznie wpiszesz dane wejściowe, przekazanie danych może zostać opóźnione. Np. W powłoce cmd.exe systemu Windows dane są buforowane w powłoce cmd.exe. Dane są przekazywane do wykonującego się programu java tylko po powrocie karetki (control-m lub <enter>
). To jest ograniczenie środowiska wykonawczego. Oczywiście InputStream.available () zwraca 0 tak długo, jak długo powłoka buforuje dane - to poprawne zachowanie; w tym momencie nie ma dostępnych danych. Gdy tylko dane są dostępne z powłoki, metoda zwraca wartość> 0. NB: Cygwin używa cmd.
Najprostsze rozwiązanie (bez blokowania, więc nie jest wymagany limit czasu)
Po prostu użyj tego:
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.
LUB równoważnie,
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);
}
Bogatsze rozwiązanie (maksymalnie wypełnia bufor w określonym czasie)
Oświadcz, że:
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;
}
Następnie użyj tego:
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.
is.available() > 1024
ta sugestia się nie powiedzie. Z pewnością istnieją strumienie, które zwracają zero. SSLSockets do niedawna. Nie możesz na tym polegać.Zakładając, że twój strumień nie jest obsługiwany przez gniazdo (więc nie możesz go użyć
Socket.setSoTimeout()
), myślę, że standardowym sposobem rozwiązania tego typu problemu jest użycie Future.Załóżmy, że mam następujący moduł wykonawczy i strumienie:
Mam program zapisujący, który zapisuje dane, a następnie czeka 5 sekund przed zapisaniem ostatniej części danych i zamknięciem strumienia:
Normalny sposób czytania tego jest następujący. Odczyt będzie blokowany na czas nieokreślony dla danych, więc kończy się to w ciągu 5 sekund:
które wyjścia:
Gdyby istniał bardziej podstawowy problem, jak brak odpowiedzi pisarza, czytelnik zablokowałby się na zawsze. Jeśli zawiniesz odczyt w przyszłości, będę mógł kontrolować limit czasu w następujący sposób:
które wyjścia:
Mogę przechwycić wyjątek TimeoutException i wykonać dowolne czyszczenie.
źródło
executer.shutdownNow
nie zabije wątku. Będzie próbował ją przerwać, ale bez efektu. Nie ma możliwości czyszczenia i jest to poważny problem.Jeśli Twój InputStream jest obsługiwany przez Socket, możesz ustawić limit czasu Socket (w milisekundach) za pomocą setSoTimeout . Jeśli wywołanie read () nie odblokuje się w określonym czasie, zgłosi SocketTimeoutException.
Po prostu upewnij się, że wywołujesz setSoTimeout na Socket przed wykonaniem wywołania read ().
źródło
Wolałbym raczej zakwestionować stwierdzenie problemu niż po prostu przyjąć je na ślepo. Potrzebujesz tylko limitów czasu z konsoli lub przez sieć. Jeśli masz to drugie,
Socket.setSoTimeout()
iHttpURLConnection.setReadTimeout()
które oboje robią dokładnie to, co jest wymagane, o ile skonfigurujesz je poprawnie podczas ich konstruowania / zdobywania. Pozostawienie tego w dowolnym punkcie później w aplikacji, gdy wszystko, co masz, to kiepski projekt InputStream, co prowadzi do bardzo niewygodnej implementacji.źródło
Nie korzystałem z klas z pakietu Java NIO, ale wygląda na to, że mogą być tutaj pomocne. W szczególności java.nio.channels.Channels i java.nio.channels.InterruptibleChannel .
źródło
Oto sposób na pobranie NIO FileChannel z System.in i sprawdzenie dostępności danych przy użyciu limitu czasu, co jest szczególnym przypadkiem problemu opisanego w pytaniu. Uruchom go na konsoli, nie wpisuj żadnych danych wejściowych i czekaj na wyniki. Został pomyślnie przetestowany pod Javą 6 w systemach Windows i Linux.
Co ciekawe, podczas uruchamiania programu w NetBeans 6.5, a nie na konsoli, limit czasu w ogóle nie działa, a wywołanie System.exit () jest faktycznie konieczne, aby zabić wątki zombie. Dzieje się tak, że wątek przerywacza blokuje (!) Wywołanie funkcji reader.interrupt (). Inny program testowy (tutaj nie pokazany) dodatkowo próbuje zamknąć kanał, ale to też nie działa.
źródło
Jak powiedziałem, NIO jest najlepszym (i poprawnym) rozwiązaniem. Jeśli jednak naprawdę utkniesz w InputStream, możesz też
Stwórz wątek, którego wyłącznym zadaniem jest odczyt z InputStream i umieszczenie wyniku w buforze, który można odczytać z oryginalnego wątku bez blokowania. Powinno to działać dobrze, jeśli masz tylko jedną instancję strumienia. W przeciwnym razie możesz zabić wątek przy użyciu przestarzałych metod w klasie Thread, chociaż może to spowodować wycieki zasobów.
Polegaj na isAvailable, aby wskazać dane, które można odczytać bez blokowania. Jednak w niektórych przypadkach (na przykład w przypadku gniazd) może zająć potencjalnie blokujący odczyt dla isAvailable, aby zgłosić coś innego niż 0.
źródło
Socket.setSoTimeout()
jest równie poprawnym i znacznie prostszym rozwiązaniem. LubHttpURLConnection.setReadTimeout()
.Zainspirowany tą odpowiedzią wymyśliłem nieco bardziej obiektowe rozwiązanie.
Jest to ważne tylko wtedy, gdy zamierzasz czytać znaki
Możesz zastąpić BufferedReader i zaimplementować coś takiego:
Oto prawie kompletny przykład.
Zwracam 0 dla niektórych metod, powinieneś zmienić to na -2, aby spełnić twoje potrzeby, ale myślę, że 0 jest bardziej odpowiednie z kontraktem BufferedReader. Nic się nie stało, po prostu odczytano 0 znaków. Metoda readLine jest strasznym zabójcą wydajności. Powinieneś utworzyć całkowicie nowy BufferedReader, jeśli faktycznie chcesz używać readLin e. W tej chwili nie jest bezpieczny wątkowo. Jeśli ktoś wywoła operację, gdy readLines czeka na wiersz, przyniesie to nieoczekiwane wyniki
Nie lubię wracać -2 gdzie jestem. Rzuciłbym wyjątek, ponieważ niektórzy ludzie mogą po prostu sprawdzać, czy int <0, aby rozważyć EOS. W każdym razie te metody twierdzą, że „nie można blokować”, należy sprawdzić, czy to stwierdzenie jest rzeczywiście prawdziwe i po prostu ich nie zastępować.
źródło