Java InputStream blockiert Lesen

Lesezeit: 12 Minuten

Java InputStream blockiert Lesen
jbu

Laut der Java-API ist die InputStream.read() wird beschrieben als:

Wenn kein Byte verfügbar ist, weil das Ende des Streams erreicht wurde, wird der Wert -1 zurückgegeben. Diese Methode blockiert, bis Eingabedaten verfügbar sind, das Ende des Streams erkannt oder eine Ausnahme ausgelöst wird.

Ich habe ein while(true) Schleife, die einen Lesevorgang durchführt, und ich bekomme immer -1, wenn nichts über den Stream gesendet wird. Das wird erwartet.

Meine Frage ist, wann würde read() jemals blockieren? Denn wenn es keine Daten erhält, gibt es -1 zurück. Ich würde erwarten, dass ein blockierender Lesevorgang wartet, bis Daten empfangen werden. Wenn Sie das Ende des Eingabestreams erreicht haben, sollte read() nicht einfach auf Daten warten, anstatt -1 zurückzugeben?

Oder blockiert read() nur, wenn ein anderer Thread auf den Stream zugreift und Ihr read() nicht auf den Stream zugreifen kann?


Was mich zu meiner nächsten Frage führt. Früher hatte ich einen Ereignis-Listener (von meiner Bibliothek bereitgestellt), der mich benachrichtigte, wenn Daten verfügbar waren. Wenn ich benachrichtigt wurde, würde ich anrufen while((aByte = read()) > -1) speichert das Byte. Ich war verwirrt, als ich ZWEI Ereignisse in unmittelbarer zeitlicher Nähe erhielt und nicht alle meine Daten angezeigt wurden. Es schien, als würde nur das hintere Ende der Daten des zweiten Ereignisses angezeigt und der Rest fehlte.

Irgendwann habe ich meinen Code so geändert, dass ich angerufen habe, wenn ich ein Ereignis erhalte if(inputStream.available() > 0) while((aByte = read()) > -1) speichert das Byte. Jetzt funktionierte es einwandfrei und alle meine Daten wurden angezeigt.

Kann sich jemand dieses Verhalten erklären? Der InputStream.available() soll die Anzahl der Bytes zurückgeben, die Sie lesen können, bevor der nächste Aufrufer (des Streams?) Blockiert wird. Selbst wenn ich .available() nicht verwende, würde ich erwarten, dass das Lesen des ersten Ereignisses nur das Lesen des zweiten Ereignisses blockiert, aber nicht zu viele Stream-Daten löscht oder verbraucht. Warum werden dadurch nicht alle meine Daten angezeigt?

  • Sie verwirren das Ende des Streams, da derzeit keine Daten verfügbar sind. Wenn derzeit keine Daten verfügbar sind, blockiert es. Am Ende des Streams wird -1 zurückgegeben.

    – Benutzer207421

    5. März 19 um 8:36 Uhr

Java InputStream blockiert Lesen
Erickson

Die zugrunde liegende Datenquelle für einige Implementierungen von InputStream kann signalisieren, dass das Ende des Streams erreicht ist und keine Daten mehr gesendet werden. Bis dieses Signal empfangen wird, können Lesevorgänge auf einem solchen Strom blockieren.

Zum Beispiel ein InputStream von einem Socket socket blockiert, anstatt EOF zurückzugeben, bis ein TCP-Paket mit gesetztem FIN-Flag empfangen wird. Wenn EOF von einem solchen Stream empfangen wird, können Sie sicher sein, dass alle auf diesem Socket gesendeten Daten zuverlässig empfangen wurden und Sie keine weiteren Daten lesen können. (Wenn andererseits ein blockierender Lesevorgang zu einer Ausnahme führt, sind möglicherweise einige Daten verloren gegangen.)

Anderen Streams, wie denen aus einer Rohdatei oder einem seriellen Anschluss, fehlt möglicherweise ein ähnliches Format oder Protokoll, um anzuzeigen, dass keine weiteren Daten verfügbar sind. Solche Streams können sofort EOF (-1) zurückgeben, anstatt zu blockieren, wenn derzeit keine Daten verfügbar sind. In Ermangelung eines solchen Formats oder Protokolls können Sie jedoch nicht sicher sein, wann die andere Seite mit dem Senden von Daten fertig ist.


In Bezug auf Ihre zweite Frage klingt es so, als hätten Sie möglicherweise eine Race Condition gehabt. Ohne den fraglichen Code zu sehen, vermute ich, dass das Problem tatsächlich in Ihrer Methode der “Anzeige” lag. Vielleicht hat der Versuch, durch die zweite Benachrichtigung anzuzeigen, die Arbeit, die während der ersten Benachrichtigung geleistet wurde, irgendwie zunichte gemacht.

  • Ich verwende den Inputstream für einen COM-Port. Wenn ich eine While-Schleife eines Lesevorgangs durchführe, wird immer -1 zurückgegeben, bis das Gerät (das andere Ende des COM-Ports) Daten ausgibt. Ich muss den Stream nie wieder öffnen. Daher ergibt deine Erklärung für mich keinen Sinn. Öffnet das Gerät den Stream jedes Mal neu?

    – jbu

    4. März 09 um 18:16 Uhr

  • rxtx … eine Java-Bibliothek zur Verbindung mit seriellen und parallelen Ports

    – jbu

    4. März 09 um 18:18 Uhr

  • Hmm, ich habe nur javax.comm verwendet, das InputStream wie beabsichtigt implementiert. Es ist möglich, dass die rxtx-Implementierung den Vertrag von InputStream.read() verletzt.

    – Ericsson

    4. März 09 um 18:20 Uhr

  • vielleicht liegt es am gerät. Das Gerät verwendet einen COM/Serial-Port, der mit einer USB-Schnittstelle umschlossen ist. Es ist eine seltsame Schnittstelle.

    – jbu

    4. März 09 um 18:32 Uhr

  • Und nein, ich glaube nicht, dass das Display das Problem war. Ich gebe einfach die Daten des Streams über den system.out-Stream aus.

    – jbu

    4. März 09 um 18:35 Uhr

Es gibt -1 zurück, wenn es das Ende des Streams ist. Wenn der Stream noch offen ist (dh Socket-Verbindung), aber keine Daten die lesende Seite erreicht haben (Server ist langsam, Netzwerke sind langsam, …), blockiert read().

Call available() ist nicht erforderlich. Es fällt mir schwer, Ihr Benachrichtigungsdesign zu verstehen, aber Sie benötigen keine Aufrufe außer read() selbst. Die Methode available() dient nur der Bequemlichkeit.

  • Es ist seltsam, dass ich available() brauche, um dort zu sein, sonst bekomme ich nicht alle meine Daten. Können Sie erklären, warum ich available() brauche?

    – jbu

    4. März 09 um 18:17 Uhr

  • (Verfügbar ist nicht einmal garantiert, dass irgendetwas bis 0 zurückgegeben wird.)

    – Tom Hawtin – Angelleine

    4. März 09 um 18:20 Uhr

1642579514 6 Java InputStream blockiert Lesen
Guss

OK, das ist ein bisschen chaotisch, also lasst uns das zuerst klären: InputStream.read() Sperren hat nichts mit Multithreading zu tun. Wenn Sie mehrere Threads haben, die aus demselben Eingabestrom lesen, und Sie zwei sehr nahe beieinander liegende Ereignisse auslösen, bei denen jeder Thread versucht, ein Ereignis zu verbrauchen, kommt es zu einer Beschädigung: Der erste zu lesende Thread erhält einige Bytes (möglicherweise alle die Bytes) und wenn der zweite Thread geplant wird, liest er die restlichen Bytes. Wenn Sie vorhaben, einen einzelnen IO-Stream in mehr als einem Thread zu verwenden, immer synchronized() {} auf irgendeinen äußeren Zwang.

Zweitens, wenn Sie von Ihrem lesen können InputStream bis Sie -1 erhalten und dann warten und später erneut lesen können, dann ist die von Ihnen verwendete InputStream-Implementierung defekt! Der Vertrag für InputStream sagt deutlich, dass ein InputStream.read() sollte nur -1 zurückgeben, wenn keine Daten mehr zu lesen sind, da das Ende des gesamten Streams erreicht wurde und keine Daten mehr verfügbar sein werden – wie wenn Sie aus einer Datei lesen und das Ende erreichen.

Das Verhalten für “Jetzt sind keine Daten mehr verfügbar, bitte warten Sie und Sie erhalten mehr” ist für read() zu blockieren und nicht zurückzugeben, bis einige Daten verfügbar sind (oder eine Ausnahme ausgelöst wird).

  • Gute Erklärung. Ich fange an zu glauben, dass die von mir verwendete Inputstream-Implementierung defekt ist.

    – jbu

    4. März 09 um 18:26 Uhr

1642579514 902 Java InputStream blockiert Lesen
Robert

Standardmäßig ist das Verhalten des bereitgestellten RXTX InputStream nicht konform.

Sie müssen den Empfangsschwellenwert auf 1 setzen und das Empfangs-Timeout deaktivieren:

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

Quelle: Serielle RXTX-Verbindung – Problem beim Blockieren von read()

Ja! Gib deinen Stream noch nicht auf Jbu. Wir sprechen hier von serieller Kommunikation. Für serielles Zeug wird absolut erwartet, dass eine -1 beim Lesen zurückgegeben werden kann/wird, aber dennoch Daten zu einem späteren Zeitpunkt erwartet. Das Problem ist, dass die meisten Leute daran gewöhnt sind, mit TCP/IP umzugehen, das immer eine 0 zurückgeben sollte, es sei denn, TCP/IP wird getrennt … dann macht -1 Sinn. Bei Serial gibt es jedoch über einen längeren Zeitraum keinen Datenfluss und kein “HTTP Keep Alive” oder TCP/IP-Heartbeat oder (in den meisten Fällen) keine Hardware-Flusskontrolle. Aber die Verbindung ist physisch und immer noch durch “Kupfer” verbunden und immer noch perfekt live.

Nun, wenn das, was sie sagen, richtig ist, dh: Serial sollte auf einer -1 geschlossen werden, warum müssen wir dann nach Dingen wie OnCTS, pmCarroerDetect, onDSR, onRingIndicator usw. Ausschau halten? Verdammt, wenn 0 bedeutet, dass es da ist , und -1 bedeutet es nicht, dann scheiß auf all diese Erkennungsfunktionen! 🙂

Das Problem, mit dem Sie möglicherweise konfrontiert sind, kann woanders liegen.

Nun zu den Besonderheiten:

F: “Es schien, als würde nur das hintere Ende der Daten des zweiten Ereignisses angezeigt und der Rest fehlte.”

A: Ich vermute, dass Sie in einer Schleife waren und dasselbe Byte wiederverwendeten[] Puffer. Die 1. Nachricht kommt herein, wird noch nicht auf dem Bildschirm angezeigt/log/std out (weil Sie in der Schleife sind), dann lesen Sie die 2. Nachricht und ersetzen die Daten der 1. Nachricht im Puffer. Nochmals, weil ich vermute, dass Sie nicht speichern, wie viel Sie gelesen haben, und dann sichergestellt haben, dass Sie Ihren Speicherpuffer um die vorherige Lesemenge ausgleichen.

F: „Ich habe meinen Code schließlich so geändert, dass ich if(inputStream.available() > 0) aufgerufen habe, wenn ich ein Ereignis erhalte, während ((aByte = read()) > -1) das Byte gespeichert habe.“

A: Bravo… das ist das gute Zeug dort. Nun, Ihr Datenpuffer befindet sich in einer IF-Anweisung, Ihre 2. Nachricht wird Ihre 1. nicht verstopfen … nun, eigentlich war es wahrscheinlich nur eine große (er) Nachricht an der 1. Stelle. Aber jetzt lesen Sie alles auf einmal und behalten die Daten intakt.

C: “… Rennbedingung …”

A: Ahhh, der gute alte Sündenbock! Die Race-Condition… 🙂 Ja, das kann eine Race-Condition gewesen sein, vielleicht sogar. Aber es könnte auch nur die Art und Weise sein, wie der RXTX die Flagge löscht. Das Löschen des Flags „Daten verfügbar“ kann möglicherweise nicht so schnell erfolgen, wie man erwartet. Kennt jemand zum Beispiel den Unterschied zwischen read VS readLine in Bezug auf das Löschen des Puffers, in dem die Daten zuvor gespeichert wurden, und das Zurücksetzen des Ereignis-Flags? Ich auch nicht. 🙂 Ich kann die Antwort auch noch nicht finden… aber… lassen Sie mich noch ein paar Sätze weiterschweifen. Die ereignisgesteuerte Programmierung weist noch einige Mängel auf. Lassen Sie mich Ihnen ein Beispiel aus der realen Welt geben, mit dem ich mich kürzlich auseinandersetzen musste.

  • Ich habe einige TCP/IP-Daten, sagen wir mal 20 Bytes.
  • Also erhalte ich das OnEvent für Received Data.
  • Ich beginne mein “Lesen” sogar auf den 20 Bytes.
  • Bevor ich meine 20 Bytes fertig gelesen habe, bekomme ich weitere 10 Bytes.
  • Das TCP/IP versucht jedoch, mich zu benachrichtigen, oh, sieht, dass das Flag immer noch GESETZT ist, und wird mich nicht erneut benachrichtigen.
  • Ich beende jedoch das Lesen meiner 20 Bytes (verfügbar () sagte, es gäbe 20) …
  • … und die letzten 10 Bytes bleiben im TCP/IP Q … weil sie mir nicht mitgeteilt wurden.

Sehen Sie, die Benachrichtigung wurde verpasst, weil das Flag immer noch gesetzt war … obwohl ich begonnen hatte, die Bytes zu lesen. Hätte ich die Bytes beendet, wäre das Flag gelöscht worden, und ich hätte eine Benachrichtigung für die nächsten 10 Bytes erhalten.

Das genaue Gegenteil von dem, was jetzt für Sie passiert.

Also ja, gehen Sie mit einem IF available () … lesen Sie die zurückgegebene Datenlänge. Wenn Sie dann paranoid sind, stellen Sie einen Timer ein und rufen Sie available() erneut auf. Wenn dort noch Daten vorhanden sind, lesen Sie die neuen Daten nicht. Wenn available() 0 (oder -1) zurückgibt, dann entspannen Sie sich … lehnen Sie sich zurück … und warten Sie auf die nächste OnEvent-Benachrichtigung.

  • Falsch. Sie verletzen den InputStream-Vertrag. Per Definition erreicht man die Ende des Streams bedeutet, dass keine Daten mehr kommen. Sie können keine weiteren Daten zurückgeben, wenn Sie das Ende erreicht haben. Das ist einfach nur Englisch.

    – Gili

    5. Juli 11 um 18:54 Uhr

1642579514 260 Java InputStream blockiert Lesen
Gemeinschaft

InputStream ist nur eine abstrakte Klasse, leider entscheidet die Umsetzung was passiert.

Was passiert, wenn nichts gefunden wird:

  • Steckdosen (dh SocketInputStream) wird blockiert, bis Daten empfangen werden (standardmäßig). Aber es ist möglich, ein Timeout einzustellen (siehe: setSoTimeout), dann ist die read blockiert für x ms. Wenn immer noch nichts empfangen wird, dann a SocketTimeoutException wird geworfen.

    Aber mit oder ohne Timeout, Lesen von a SocketInputStream kann manchmal zu einem führen -1. (ZB wenn sich mehrere Clients gleichzeitig mit demselben verbinden host:port, dann, obwohl die Geräte verbunden zu sein scheinen, das Ergebnis von a read könnte sofort in a resultieren -1 (keine Rückgabe von Daten).)

  • Serialio Die Kommunikation wird immer zurückkehren -1; Sie können auch eine Zeitüberschreitung festlegen (verwenden Sie setTimeoutRx), das read wird zuerst für x ms blockieren, aber das Ergebnis wird immer noch sein -1 wenn nichts gefunden wird. (Anmerkung: Es sind jedoch mehrere serielle I/O-Klassen verfügbar, das Verhalten kann herstellerabhängig sein.)

  • Dateien (Reader oder Streams) führt zu einem EOFException.

Arbeiten Sie an einer generischen Lösung:

  • Wenn Sie einen der oben genannten Streams in a DataInputStream, dann können Sie Methoden wie verwenden readByte, readChar, etc . Alle -1 Werte umgewandelt werden EOFException. (PS: Wenn Sie viele kleine Lesevorgänge ausführen, ist es eine gute Idee, sie in eine BufferedInputStream Erste)
  • Beide SocketTimeoutException und EOFException erweitern IOException , und es sind mehrere andere möglich IOException‘S. Es ist bequem, einfach nachzusehen IOException‘s, um Kommunikationsprobleme zu erkennen.

Ein weiteres heikles Thema ist die Spülung. flush in Bezug auf Sockets bedeutet es “jetzt senden”, aber in Bezug auf Serialio bedeutet es “Puffer verwerfen”.

  • Falsch. Sie verletzen den InputStream-Vertrag. Per Definition erreicht man die Ende des Streams bedeutet, dass keine Daten mehr kommen. Sie können keine weiteren Daten zurückgeben, wenn Sie das Ende erreicht haben. Das ist einfach nur Englisch.

    – Gili

    5. Juli 11 um 18:54 Uhr

1642579514 314 Java InputStream blockiert Lesen
Manju

Ich denke, Sie können den gesamten Datenstrom erhalten, wenn Sie thread.sleep() verwenden.

  • Dies schläft nur den Thread, der thread.sleep() aufgerufen hat. Durch Aufrufen von thread.sleep() werden keine Daten gelesen / empfangen / behandelt. Das einzige, was passiert, ist, dass der Anruf in der Hoffnung schläft, dass mehr Daten kommen.

    – hodder

    8. November 11 um 22:33 Uhr

.

545080cookie-checkJava InputStream blockiert Lesen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy