Est-il possible de lire à partir d'un InputStream avec un délai d'attente?
Plus précisément, le problème est d'écrire une méthode comme ceci:
int maybeRead(InputStream in, long timeout)
où la valeur de retour est la même que dans.read() si les données sont disponibles au sein de 'timeout' millisecondes, et 2 sinon. Avant le retour de la méthode, tout engendré fils doit quitter.
Pour éviter les disputes, le sujet ici java.io.InputStream, tel que documenté par le Soleil (n'importe quel version de Java). Veuillez noter que ce n'est pas aussi simple qu'il y paraît. Ci-dessous sont quelques-uns des faits qui sont pris en charge directement par le Soleil de la documentation.
-
La dans.méthode read() peut être non-interruptible.
-
Emballage de la InputStream dans un Lecteur ou InterruptibleChannel ne vous aide pas, parce que toutes les classes peuvent faire est d'appeler les méthodes de l'InputStream. S'il était possible d'utiliser ces classes, il serait possible d'écrire une solution qui ne fera qu'exécuter la même logique directement sur la InputStream.
-
Il est toujours acceptable pour dans.disponible() renvoient 0.
-
La dans.la méthode close() peut bloquer ou de ne rien faire.
-
Il n'existe pas de façon de tuer un autre thread.
Vous devez vous connecter pour publier un commentaire.
À l'aide de inputStream.disponible (en)
J'ai trouvé le contraire - elle renvoie toujours la meilleure valeur pour le nombre d'octets disponibles. Javadoc pour
InputStream.available()
:Une estimation est inévitable en raison de la synchronisation/insipidité. Le chiffre peut être une sous-estimer parce que de nouvelles données arrivent constamment. Cependant il toujours "rattrape" sur le prochain appel - il doit tenir compte de tous arrivés de données, d'un bar qui arrivent juste au moment du nouvel appel. Définitivement de retour 0 quand il y a des données ne sont pas la condition ci-dessus.
Première mise en garde: Béton sous-classes de InputStream sont responsables de disponible()
InputStream
est une classe abstraite. Il n'a pas de source de données. C'est vide de sens pour elle d'avoir à disposition des données. Par conséquent, javadoc pouravailable()
déclare également:Et en effet, le béton flux d'entrée des classes de faire remplacer disponibles(), en fournissant des valeurs significatives, pas de constante 0.
Deuxième mise en garde: assurez-vous d'utiliser le transport-retour lors de la saisie dans Windows.
Si vous utilisez
System.in
, votre programme ne reçoit les commentaires lors de votre commande shell mains il. Si vous utilisez un fichier de redirection/tuyaux (par exemple unfichier > java myJavaApp ou somecommand | java myJavaApp ), puis les données d'entrée sont généralement remis immédiatement. Toutefois, si vous avez manuellement le type d'entrée, les données de transfert peut être retardée. E. g. Avec windows cmd.exe shell, les données sont mises en mémoire tampon à l'intérieur de cmd.exe shell. Les données ne sont transmises à l'exécution de java le programme de retour de chariot (control-m ou<enter>
). C'est une limitation de l'environnement d'exécution. Bien sûr, InputStream.disponible() renvoie 0 pour aussi longtemps que le shell en mémoire tampon les données, c'est le comportement correct; il n'y a pas de données disponibles à ce point. Dès que les données sont disponibles à partir de la coquille, la méthode renvoie une valeur > 0. NB: utilise Cygwin cmd.exe trop.Solution la plus simple (pas de blocage, donc pas de délai d'attente requis)
Utiliser seulement ceci:
OU, de manière équivalente,
Solution plus riche (au maximum remplit tampon à l'intérieur de délai d'attente)
Déclarer ceci:
Puis utilisez ceci:
is.available() > 1024
cette suggestion sera un échec. Il y a certainement des flux qui ne retournent pas de zéro. SSLSockets par exemple, jusqu'à récemment. Vous ne pouvez pas compter sur cela.SSLSocket.getInputStream().available()
retourné à zéro; nulle part ai-je affirmé que cette " violé le contrat deavailable()
'; nulle part je n'ai affirmé que "le contrat deInputStream
changé": ce sont tous vos affirmations; pas de mine.En supposant que le flux n'est pas soutenu par une prise (de sorte que vous ne pouvez pas utiliser
Socket.setSoTimeout()
), je pense que le niveau moyen de résoudre ce type de problème est d'utiliser un Avenir.Supposons que j'ai la suite exécuteur testamentaire et de ruisseaux:
J'ai un écrivain qui écrit des données puis attend 5 secondes avant d'écrire le dernier morceau de données et la fermeture du flux:
La façon normale de lecture se présente comme suit. La lecture de bloquer indéfiniment pour des données et ainsi ce termine dans 5s:
sorties:
Si il y a un problème plus fondamental, comme l'écrivain ne répond pas, le lecteur devrait bloquer pour toujours.
Si j'enveloppe le lire dans un avenir, je peux contrôler le délai d'attente comme suit:
sorties:
Je peux attraper le TimeoutException et faire tout le nettoyage je veux.
executer.shutdownNow
ne tuera pas le fil. Il va essayer de l'interrompre, sans aucun effet. Il n'y a pas de nettoyage possible et c'est un problème sérieux.readLine()
et de faire un code similaire mais à l'intérieur decall()
deFuture
.Je m'interroge sur l'énoncé du problème plutôt que de simplement l'accepter aveuglément. Vous avez seulement besoin de délais d'attente à partir de la console ou sur le réseau. Si le dernier vous avez
Socket.setSoTimeout()
etHttpURLConnection.setReadTimeout()
qui à la fois faire exactement ce qui est nécessaire, aussi longtemps que vous les configurer correctement lors de la construction/acquisition. Laisser à l'arbitraire d'un point plus tard dans l'application lorsque tout ce que vous avez est le InputStream est une mauvaise conception menant à un très maladroit de mise en œuvre.InputStream
est de ne pas disposer d'un délai d'attente de la méthode, ni la plupart des implémentations sous-jacentes.Sockets
sont une exception remarquable et le seul que je peux penser à de la main gauche. Ignorant les faits ne va pas aider à la discussion.setSoTimeout()
n'est pas suffisant, ce serait? Et NB de votre affirmation dans cette question qu'il ne fonctionne pas pour les sockets client n'est pas correct.Si votre InputStream est soutenu par un support, vous pouvez définir un Socket délai d'attente (en millisecondes) à l'aide de setSoTimeout. Si la lecture() l'appel n'a pas débloquer dans le délai spécifié, il va lancer une SocketTimeoutException.
Assurez-vous juste que vous appelez setSoTimeout sur le support avant de le lire() de l'appel.
Je n'ai pas utilisé les classes de Java NIO paquet, mais il semble ils pourraient être d'une aide ici. Plus précisément, java.nio.les canaux.Canaux et java.nio.les canaux.InterruptibleChannel.
Ici est un moyen d'obtenir une NIO FileChannel à partir du Système.dans et de vérifier la disponibilité de données à l'aide d'un délai d'attente, ce qui est un cas particulier du problème décrit dans la question. Exécuter à la console, ce n'est pas le type de l'entrée, et attendre les résultats. Il a été testé avec succès sous Java 6 sur Windows et Linux.
Il est intéressant de noter, lors de l'exécution du programme à l'intérieur de NetBeans 6.5 plutôt qu'à la console, le délai d'attente ne fonctionne pas du tout, et l'appel Système.exit() est en fait nécessaire de tuer le zombie threads. Ce qui se passe est que le interruptor thread se bloque (!) sur l'appel au lecteur.interrupt(). Un autre programme de test (non montré ici) en outre, essaie de fermer le canal, mais même cela ne fonctionne pas.
Que jt a dit, NIO est le meilleur (et corriger) solution. Si vous êtes vraiment coincé avec un InputStream cependant, vous pouvez soit
Frayer un thread qui est exclusive de l'emploi est à lire à partir de l'InputStream et mettre le résultat dans une mémoire tampon qui peut être lu à partir de votre fil d'origine sans blocage. Cela devrait bien fonctionner si vous ne jamais avoir qu'une seule instance du flux. Sinon, vous pourriez être en mesure de tuer le thread à l'aide de l'obsolète méthodes de la classe Thread, même si cela peut causer des fuites de ressource.
Compter sur isAvailable pour indiquer que les données qui peuvent être lues sans blocage. Toutefois, dans certains cas (comme avec les Sockets), il peut prendre un potentiellement bloquant lire pour isAvailable rapport à quelque chose d'autre que 0.
Socket.setSoTimeout()
est tout aussi correct et beaucoup plus simple solution. OuHttpURLConnection.setReadTimeout()
.InputStream,
qui n'est ni sans blocage ni une pipe. Si fd 0 se trouve être d'une pipe, il peut être obtenu via le Système.inheritedChannel (), mais qui n'a rien à voir avec le Système.dans.