Bloc Java InputStream read
selon l'api java, le InputStream.read()
est décrit comme:
si aucun octet n'est disponible fin du cours d'eau a été atteint, la valeur -1 est renvoyée. Cette méthode bloque jusqu'à ce que les données d'entrée soient disponibles, la fin du flux est détecté, ou une exception est levée.
j'ai une boucle while(true)
qui fait une lecture et j'ai toujours -1 quand rien n'est envoyé au-dessus du ruisseau. Ce que l'on attend.
ma question est quand lirait () jamais bloquer? Car s'il ne reçoit pas de données il renvoie -1. Je m'attendrais à ce qu'une lecture de blocage attende la réception des données. Si vous avez atteint la fin du flux d'entrée, ne devriez-vous pas simplement lire() attendre les données au lieu de retourner -1?
ou ne bloque read() que s'il y a un autre thread accédant au flux et que votre read() ne peut pas accéder au flux?
qui m'amène à ma prochaine question. J'ai utilisé de l'écouteur d'événement (fourni par ma bibliothèque) qui m'avertir lorsque des données sont disponibles. Lorsque j'ai été avisé, j'appelais while((aByte = read()) > -1)
pour stocker l'octet. J'étais perplexe quand j'ai eu deux événements dans la proximité de temps très proche et pas toutes mes données étaient affichées. Il semblait que seule l'extrémité de queue des données du deuxième événement serait affichée et le reste manquait.
j'ai finalement changé mon code de sorte que quand j'obtiens un événement J'avais appelé if(inputStream.available() > 0) while((aByte = read()) > -1)
le magasin byte. Maintenant, cela a fonctionné correctement et toutes mes données ont été affichées.
quelqu'un peut-il expliquer ce comportement? Le InputStream.available()
, dit-retourner le nombre d'octets que vous pouvez lire avant de bloquer l'appelant suivant (cours d'eau?). Même si je ne l'utilise pas .disponible () 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 de flux. Pourquoi toutes mes données ne seraient pas affiche?
8 réponses
la source de données sous-jacente pour certaines implémentations de InputStream
peut signaler que la fin du flux a été atteinte, et aucune autre donnée ne sera envoyée. Jusqu'à ce que ce signal soit reçu, lire les opérations sur un tel flux peut bloquer.
par exemple, un InputStream
d'une socket Socket
bloquera, plutôt que de retourner EOF, jusqu'à ce qu'un paquet TCP avec le drapeau FIN soit reçu. Lorsque EOF est reçu d'un tel flux, vous pouvez être assuré que toutes les données envoyés sur cette socket a été reçu de manière fiable, et vous ne serez pas en mesure de lire les données. (Si une lecture de blocage aboutit à une exception, par contre, certaines données peuvent avoir été perdues.)
D'autres flux, comme ceux d'un fichier brut ou d'un port série, peuvent manquer d'un format ou d'un protocole similaire pour indiquer qu'aucune autre donnée ne sera disponible. Ces flux peuvent retourner immédiatement EOF (-1) au lieu de bloquer quand aucune donnée n'est actuellement disponible. En l'absence d'un tel format ou protocole, cependant, vous ne pouvez pas être sûr quand l'autre côté est fait l'envoi de données.
en ce qui concerne votre deuxième question, on dirait que vous aviez peut-être une condition de race. Sans voir le code en question, je devine que le problème réside en fait dans votre méthode d ' "affichage". Peut-être que la tentative d'afficher la deuxième notification a été en quelque sorte de saboter le travail effectué lors de la première notification.
renvoie -1 si c'est la fin du flux. Si stream est toujours ouvert (connexion socket) mais qu'aucune donnée n'a atteint le niveau de lecture (le serveur est lent, les réseaux sont lents,)...) read() blocs.
Vous n'avez pas besoin d'appeler(). J'ai du mal à comprendre votre conception de notification, mais vous n'avez pas besoin d'appels sauf read() lui-même. Méthode disponible () est là pour la commodité seulement.
OK, c'est un peu le bordel donc la première chose permet de clarifier ceci: InputStream.le blocage de read () n'a rien à voir avec le multi-threading. Si vous avez plusieurs threads lisant à partir du même flux d'entrée et que vous déclenchez deux événements très proches l'un de l'autre - où chaque thread essaye de consommer un événement, alors vous obtiendriez de la corruption: le premier thread à lire obtiendra quelques octets (éventuellement tous les octets) et lorsque le second thread est programmé, il lira le reste des octets. Si vous prévoyez pour utiliser un seul flux IO dans plus d'un thread, toujours synchronisé sur une contrainte externe.
deuxièmement, si vous pouvez lire à partir de votre entrée jusqu'à ce que vous obtenez -1 et puis attendre et peut lire à nouveau plus tard, alors l'implémentation D'entrée que vous utilisez est cassé! Le contrat pour InputStream indique clairement qu'un InputStream.read () ne devrait retourner que -1 quand il n'y a plus de données à lire parce que la fin du flux entier telle qu'elle a été atteinte et plus de données ne sera jamais soyez disponible - comme lorsque vous lisez à partir d'un fichier et que vous atteignez la fin.
le comportement de "no more data is available now, please wait and you'll get more" est pour read() de bloquer et de ne pas revenir tant qu'il n'y a pas de données disponibles (ou une exception est lancée).
par défaut le comportement de L'entrée RXTX fournie N'est pas conforme.
vous devez régler le seuil de réception à 1 et désactiver le délai de réception:
serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();
Source: RXTX connexion série - problème avec le blocage read()
Aye! Ne pas renoncer à votre flux encore Jbu. Nous parlons de communication en série ici. Pour les documents en série, on s'attend absolument à ce qu'un -1 puisse/sera retourné sur les lectures, mais on attend toujours des données plus tard. Le problème est que la plupart des gens sont habitués à traiter avec TCP/IP qui devrait toujours retourner un 0 à moins que le TCP/IP déconnecté... alors oui, -1 sens. Cependant, avec Serial il n'y a pas de flux de données pendant des périodes prolongées, et pas de "HTTP Keep Alive", ou TCP / IP heartbeat, ou (dans la plupart des cas) pas de contrôle de flux matériel. Mais le lien est physique, et toujours connecté en "cuivre" et toujours parfaitement vivre.
maintenant, si ce qu'ils disent est correct, i.e.: Serial devrait être fermé sur un -1, alors pourquoi devons-nous surveiller des choses comme OnCTS, pmCarroerDetect, onDSR, onRingIndicator, etc... Si 0 veut dire qu'il est là, et -1 qu'il ne l'est pas, alors vissez toutes ces fonctions de détection! :- )
le problème que vous pourriez rencontrer peut pondre ailleurs.
maintenant, sur les détails:
Q: "il semblait que seule la fin des données du second événement serait affichée et le reste manquait."
A: je vais deviner que vous étiez dans une boucle, en réutilisant le même octet de tampon. 1er message n'est pas affiché sur l'écran/log/std encore (parce que vous êtes dans la boucle), puis vous lisez le 2ème message, en remplacement de la 1ère données de message dans le tampon. Encore une fois, parce que je vais deviner que vous ne stockez pas tout ce que vous lisez, et vous êtes ensuite assuré de compenser votre mémoire tampon par la quantité de lecture précédente.
Q: "j'ai finalement changé mon code de sorte que lorsque je reçois un événement, j'avais appelé si(inputStream.disponible () > 0) tandis que((aByte = read ()) > -1) stocke l'octet."
A: Bravo... c'est bien le truc là. Maintenant, votre tampon de données est dans une instruction IF, votre 2ème message ne va pas assommer votre 1er... Eh bien, en fait, c'était probablement juste un grand(plus) message à la 1ère place. Mais maintenant, vous allez tout lire d'un seul coup, en gardant les données intactes.
C:"... condition de course ..."
A: Ahhh, le bon vieux attrape tous les boucs! La condition de course... :-) Oui, cela peut avoir été une condition de course, en fait, il peut avoir bien été. Mais ça pourrait aussi être la façon dont le RXTX s'efface drapeau. La compensation des données disponibles drapeau " ne peut pas arriver aussi vite que l'on attend. Par exemple, quelqu'un connaît la différence entre lire et lire en relation avec le nettoyage du tampon dans lequel les données étaient précédemment stockées et le réglage du drapeau d'événement? Moi non plus.: -) et je ne trouve pas encore la réponse... mais... permettez-moi de revenir sur quelques phrases de plus. La programmation événementielle a encore quelques défauts. Laissez-moi vous donner un exemple concret, j'ai dû faire face récemment.
- j'ai des données TCP/IP, disons, 20 octets.
- ainsi je reçois le seul événement pour les données reçues.
- je commence ma lecture, même sur les 20 octets.
- avant de finir de lire mes 20 octets... Je reçois un autre 10 octets.
- le TCP/IP cependant, cherche à me notifier, oh, voit que le drapeau est toujours activé, et ne me notifiera plus.
- cependant, je finis de lire mon 20 octets (() dit qu'il y avait 20)...
- ... et les 10 derniers octets restent dans le TCP / IP Q... parce que je n'ai pas été notifié.
voir, la notification a été manquée parce que le drapeau était toujours affiché... même si j'avais commencé à lire les octets. Si j'avais terminé les octets, alors le drapeau aurait été effacé, et j'aurais reçu la notification pour les 10 octets suivants.
L'exact opposé de ce qui est passe pour vous maintenant.
Donc, oui, allez-y avec un if() ... faire une lecture de la longueur de données. Ensuite, si vous êtes paranoïaque, réglez une minuterie et appelez disponible() à nouveau, s'il y a encore des données là-bas, alors faites une lecture non des nouvelles données. Si disponible () renvoie 0 (ou -1), alors détendez-vous... asseyez-vous... et attendez la prochaine notification.
InputStream
est juste une classe abstraite , malheureusement la mise en œuvre décide ce qui se passe.
Ce qui se passe si rien n'est trouvé:
-
les Sockets (i.e.
SocketInputStream
) seront bloqués jusqu'à ce que les données soient reçues (par défaut). Mais il est possible de définir un timeout (voir:setSoTimeout
), puis leread
se bloquera pour x ms. Si toujours rien n'est reçu alors unSocketTimeoutException
sera lancé.mais avec ou sans timeout, la lecture d'un
SocketInputStream
peut parfois conduire à un-1
. ( E. G. lorsque plusieurs clients se connectent simultanément à la mêmehost:port
, alors même si les appareils semblent connectés, le résultat d'unread
pourrait immédiatement se traduire par un-1
(ne retournant jamais les données). ) -
Serialio communication retournera toujours
-1
; Vous pouvez également définir un délai d'attente (utilisersetTimeoutRx
), leread
sera la première à bloc pour les x ms, mais le résultat sera encore-1
si rien n'est trouvé. (remarque: mais il existe plusieurs classes IO en série, le comportement pourrait dépendre du vendeur.) -
Les fichiers (lecteurs ou flux)
EOFException
.
travailler à une Solution générique:
- si vous enveloppez l'un des cours d'eau ci-dessus dans un
DataInputStream
, ensuite, vous pouvez utiliser des méthodes commereadByte
,readChar
, etc. toutes les valeurs-1
sont converties enEOFException
. (PS: Si vous effectuez beaucoup de petites lectures, alors c'est une bonne idée de l'envelopper dans unBufferedInputStream
première) - à la fois
SocketTimeoutException
etEOFException
étendreIOException
, et il ya plusieurs autres possiblesIOException
's. il est commode de simplement vérifier pourIOException
's pour détecter les problèmes de communication.
un autre sujet sensible est flushing. flush
in termes de prises signifie "envoyer maintenant", mais en termes de Serialio il signifie "jeter le tampon".
j'utilise eclipse Jetty 9.2.2. Je suis surpris de voir que 10 à 12% de mes demandes prennent environ 500 mili-secondes chacune tandis que d'autres sont traitées en 2 à 3 mili-secondes chacune. partie du code prenant
timeBufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line = null;
String postData = "";
while ((line = br.readLine()) != null){ // this is the line taking all time
postData+=line;
}
br.close();
je pense que vous pouvez recevoir l'ensemble du flux de données si vous utilisez thread.sleep ()