60 votes

Java InputStream lecture bloquante

Selon la documentation API Java, la méthode [read()](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/InputStream.html#read()) déclarée dans InputStream est décrite comme suit :

Si aucun octet n'est disponible car la fin du flux a été atteinte, la valeur -1 est renvoyée. Cette méthode bloque jusqu'à ce que des données d'entrée soient disponibles, que la fin du flux soit détectée ou qu'une exception soit levée.

J'ai une boucle while(true) exécutant un read() et j'obtiens toujours -1 lorsque rien n'est envoyé sur le flux. C'est attendu.

Ma question est la suivante : quand read() bloquerait-il ? Puisque s'il ne reçoit aucune donnée, il renvoie -1. Je m'attendrais à ce qu'un read() bloquant attende l'arrivée de données. Si vous avez atteint la fin du flux d'entrée, read() ne devrait-il pas simplement attendre les données au lieu de renvoyer -1 ?

Ou est-ce que read() bloque uniquement s'il y a un autre thread accédant au flux et que votre read() ne peut pas y accéder ?


Cela m'amène à ma question suivante. J'avais autrefois un gestionnaire d'événements (fourni par ma bibliothèque) qui me notifierait quand des données sont disponibles. Lorsque j'étais notifié, je commençais un cycle while((aByte = read()) > -1) pour stocker l'octet. J'étais perplexe lorsque j'obtenais DEUX événements en très peu de temps et que toutes mes données n'étaient pas affichées. Il semblait que seule la fin de la seconde événement serait affichée et le reste manquait.

J'ai finalement modifié mon code pour que lorsque je reçois un événement, je commence if(inputStream.available() > 0) while((aByte = read()) > -1) pour stocker l'octet. Maintenant, cela fonctionnait correctement et toutes mes données étaient affichées.

Quelqu'un pourrait-il expliquer ce comportement ? La méthode [available()](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/io/InputStream.html#available()) d'InputStream est censée renvoyer le nombre d'octets que vous pouvez lire avant de bloquer le prochain appelant (du flux ?). Même si je n'utilise pas available(), je m'attendrais à ce que la lecture du premier événement bloque simplement la lecture du second événement, mais n'efface pas ou ne consomme pas trop de données du flux. Pourquoi cela causerait-il l'affichage incomplet de mes données ?

2voto

bvdb Points 898

InputStream est juste une classe abstraite, malheureusement l'implémentation décide ce qui se passe.

Que se passe-t-il si rien n'est trouvé :

  • Les Sockets (c'est-à-dire SocketInputStream) vont bloquer jusqu'à ce que des données soient reçues (par défaut). Mais il est possible de définir une temporisation (voir : setSoTimeout), alors le read va bloquer pendant x ms. Si toujours rien n'est reçu alors une SocketTimeoutException sera lancée.

    Mais avec ou sans temporisation, lire à partir d'un SocketInputStream peut parfois donner un -1. (Par exemple, lorsque plusieurs clients se connectent simultanément au même hôte:port, alors même si les appareils semblent connectés, le résultat d'un read pourrait immédiatement retourner un -1 (ne retournant jamais de données).)

  • La communication Serialio retournera toujours -1 ; Vous pouvez également définir une temporisation (utilisez setTimeoutRx), le read va d'abord bloquer pendant x ms, mais le résultat sera toujours -1 s'il n'y a rien de trouvé. (Remarque : mais il existe plusieurs classes d'entrées/sorties série disponibles, le comportement pourrait dépendre du fabricant.)

  • Les fichiers (lecteurs ou flux) entraîneront une EOFException.

Travailler vers une solution générique :

  • Si vous enveloppez l'un des flux ci-dessus dans un DataInputStream, alors vous pouvez utiliser des méthodes comme readByte, readChar, etc. Toutes les valeurs -1 sont converties en EOFException. (PS : Si vous effectuez un grand nombre de petites lectures, il est conseillé de l'envelopper d'abord dans un BufferedInputStream)
  • Les deux SocketTimeoutException et EOFException étendent IOException, et il existe plusieurs autres IOException possibles. Il est pratique de vérifier simplement les IOException pour détecter les problèmes de communication.

Un autre sujet sensible est le vidage. flush en termes de sockets signifie "envoyer maintenant", mais en termes de Serialio cela signifie "vider le tampon".

-7voto

manju Points 1

Je pense que vous pouvez recevoir l'intégralité du flux de données si vous utilisez thread.sleep()

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X