76 votes

Connexion d'un flux d'entrée à un flux de sortie

Mise à jour dans java9 : https://docs.oracle.com/javase/9/docs/api/java/io/InputStream.html#transferTo-java.io.OutputStream-

J'ai vu des fils similaires, mais pas tout à fait ce dont j'ai besoin.

J'ai un serveur qui va essentiellement prendre les données d'un client, le client A, et les transmettre, octet par octet, à un autre client, le client B.

J'aimerais connecter mon flux d'entrée du client A avec mon flux de sortie du client B. Est-ce possible ? Quels sont les moyens de le faire ?

De plus, ces clients s'envoient des messages, qui sont quelque peu sensibles au temps, et la mise en mémoire tampon ne suffira pas. Je ne veux pas d'un tampon de 500, par exemple, et qu'un client envoie 499 octets, puis que mon serveur attende avant de transmettre les 500 octets parce qu'il n'a pas reçu le dernier octet pour remplir le tampon.

Pour l'instant, j'analyse chaque message pour trouver sa longueur, puis je lis les octets de longueur et je les transfère. Je me suis dit (et j'ai testé) que ce serait mieux que de lire un octet et de le retransmettre encore et encore, car cela serait très lent. Je ne voulais pas non plus utiliser un tampon ou une minuterie pour la raison que j'ai indiquée dans mon dernier paragraphe - je ne veux pas que les messages attendent très longtemps avant d'être transmis simplement parce que le tampon n'est pas plein.

Quelle est la meilleure façon de procéder ?

86voto

Jon Skeet Points 692016

Ce n'est pas parce que vous utilisez un tampon que le flux doit nécessairement remplir ce tampon. En d'autres termes, ça devrait aller :

public static void copyStream(InputStream input, OutputStream output)
    throws IOException
{
    byte[] buffer = new byte[1024]; // Adjust if you want
    int bytesRead;
    while ((bytesRead = input.read(buffer)) != -1)
    {
        output.write(buffer, 0, bytesRead);
    }
}

Cela devrait fonctionner correctement. read L'appel sera bloqué jusqu'à ce qu'il y ait un peu de données disponibles, mais il n'attendra pas qu'il soit todo disponibles pour remplir le tampon. (Je suppose que cela pourrait, et je crois FileInputStream généralement sera remplir le tampon, mais un flux attaché à une socket est plus susceptible de vous donner les données immédiatement).

Je pense que cela vaut la peine d'essayer au moins cette solution simple en premier lieu.

0 votes

Oui, je pense que cela clarifie les choses. Je pense que j'ai confondu avec readFully() qui nécessite le remplissage du tampon.

1 votes

J'ai essayé votre code et j'ai aussi essayé de lire message par message en lisant la longueur du message puis en faisant un byte[] buf = longueur ; inputstream.read(buf)..... Cette dernière méthode était plus rapide, et je ne sais pas trop pourquoi. Elle semble exécuter plus de lignes de code et pourtant elle est plus rapide. Presque deux fois plus rapide.

0 votes

De plus, comme mon serveur a plusieurs sources et un seul puits, je pense que je dois lire messages au sein des sources multiples, car la simple lecture et le transfert des tampons peuvent intercaler les messages entre les clients et les brouiller.

77voto

Dean Hiller Points 5292

Et si vous utilisiez simplement

void feedInputToOutput(InputStream in, OutputStream out) {
   IOUtils.copy(in, out);
}

et en finir avec ça ?

de la bibliothèque i/o de jakarta apache commons qui est utilisée par une quantité énorme de projets déjà, donc vous avez probablement déjà le jar dans votre classpath.

23 votes

Ou simplement utiliser la fonction elle-même, puisque l'appel d'une autre fonction avec exactement les mêmes paramètres n'est pas nécessaire.....

0 votes

Oui, c'est ce que je fais personnellement. Je suppose que j'ai seulement tapé le nom de la méthode supplémentaire comme documentation mais ce n'est pas nécessaire.

1 votes

D'après ce que je peux dire, cette méthode est bloquante jusqu'à ce que l'entrée while soit passée. Par conséquent, cela devrait être fait dans un thread asynchrone pour l'auteur de la question.

22voto

JesusFreke Points 7447

Pour être complet, goyave dispose également d'un utilité pratique pour cette

ByteStreams.copy(input, output);

11voto

Stephan Points 10596

Vous pouvez utiliser un tampon circulaire :

Code

// buffer all data in a circular buffer of infinite size
CircularByteBuffer cbb = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE);
class1.putDataOnOutputStream(cbb.getOutputStream());
class2.processDataFromInputStream(cbb.getInputStream());

Dépendance Maven

<dependency>
    <groupId>org.ostermiller</groupId>
    <artifactId>utils</artifactId>
    <version>1.07.00</version>
</dependency>

Détails du mode

http://ostermiller.org/utils/CircularBuffer.html

4voto

camickr Points 137095

PipedInputStream, PipedOutputStream

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