2010-03-01 5 views
0

Le problème que j'ai est que lorsque j'utilise un InputStream pour lire des octets, il bloque jusqu'à ce que la connexion soit terminée. EG:Java: Gestion correcte des E/S de réseau?

 InputStream is = socket.getInputStream(); 
     byte[] buffer = new byte[20000]; 
     while (is.read(buffer) != -1) { 
      System.out.println("reading"); 
     } 
     System.out.println("socket read"); 

« socket lire » n'imprime pas jusqu'à ce que le paquet de Fionie est en fait recieved, fermant ainsi la connexion. Quelle est la bonne façon de recevoir tous les octets sans bloquer et attendre que la connexion tombe?

Répondre

1

Essayez ceci:

 InputStream is = socket.getInputStream(); 
     byte[] buffer = new byte[20000]; 
     int bytesRead; 
     do { 
      System.out.println("reading"); 
      bytesRead = is.read(buffer); 
     } 
     while (is.available() > 0 && bytesRead != -1); 
     System.out.println("socket read"); 

Plus d'infos: http://java.sun.com/j2se/1.4.2/docs/api/java/io/InputStream.html#available()

+0

Bonne réponse, bienvenue à SO – Zombies

+0

lorsque vous avez compris que cela ne fonctionne pas toujours, revisiter ma réponse –

+0

Ne devrait pas appel à 'is.available()' se produire avant le 'est. read() 'call, étant donné que' is.read() 'pourrait bloquer si aucun octet n'est disponible lors de la première lecture? –

2

Jetez un oeil à java.nio qui a non-blocking IO soutien.

+0

cela ressemble à une surpuissance majeure à ce stade.Je pense qu'il devrait d'abord maîtriser le blocage avant même de considérer io non bloquant. –

1

Exemple tiré de exampledepot on java.nio

// Create a direct buffer to get bytes from socket. 
// Direct buffers should be long-lived and be reused as much as possible. 
ByteBuffer buf = ByteBuffer.allocateDirect(1024); 

try { 
    // Clear the buffer and read bytes from socket 
    buf.clear(); 
    int numBytesRead = socketChannel.read(buf); 

    if (numBytesRead == -1) { 
     // No more bytes can be read from the channel 
     socketChannel.close(); 
    } else { 
     // To read the bytes, flip the buffer 
     buf.flip(); 

     // Read the bytes from the buffer ...; 
     // see Getting Bytes from a ByteBuffer 
    } 
} catch (IOException e) { 
    // Connection may have been closed 
} 

Assurez-vous de comprendre flipping tampon, car il provoque beaucoup de maux de tête. Fondamentalement, vous devez inverser votre tampon pour en lire. Si vous devez réutiliser ce tampon pour que le socket puisse y écrire, vous devez le retourner. Cependant clear() réinitialise la direction du tampon.

+0

Suivi: y a-t-il un moyen d'obtenir un blocage? Je veux bloquer jusqu'à ce qu'il se connecte, mais en utilisant while (! SChannel.finishedConnect()) coûte beaucoup de cycles de cpu. – Zombies

+0

Sélectionnez OP_CONNECT et vérifiez finishConnect() quand vous l'obtenez, ou bien connectez-vous en mode bloquant. – EJP

2

Avec des prises traditionnelles du point est que vous ne sont généralement pas voulez qu'ils bloquer: ce que vous faites quand logiquement vous ne voulez pas que votre programme de bloc est que vous mettez votre lecture/écriture de code dans un autre thread, de sorte que la séparer les blocs de thread de lecture/écriture, mais pas tout votre programme. A défaut, vous pouvez utiliser la méthode available() pour voir si des entrées sont réellement disponibles avant la lecture. Mais alors vous devez faire attention à ne pas vous asseoir dans une boucle de gravure de CPU en appelant constamment available().

Edit: si le problème est que vous êtes heureux de bloquer jusqu'à ce que les octets sont arrivés, mais pas jusqu'à ce que la connexion ait baissé (et qui est ce qui est happeningh), alors vous devez rendre le client au l'autre extrémité appelle flush() sur son flux de sortie après avoir envoyé les octets.

+0

Oui, corrigez ce dernier point de gravure de la CPU. Existe-t-il une méthode opitmisée qui ne me blesse pas avec les cycles de cpu ou le changement de contexte (Thread.sleep (long) + polling provoque-t-il une mauvaise performance?) – Zombies

+0

L'appel de Thread.sleep() entraînera une perte de performance pour l'I/O (par exemple, vous pouvez dormir pendant 500 millis mais les données sont prêtes après 100 millis, et vous ne les recevez pas pour 400 autres.) En termes de changement de contexte, quelques C/S supplémentaires par seconde n'ont pas vraiment d'importance. Mais je ne suis toujours pas clair: pourquoi ne pas laisser le bloc de socket (dans son propre fil si nécessaire)? –

+0

Désolé, je veux en fait bloquer le premier tour, lire les octets, et mon problème était mon manque de familiarité avec java IO depuis que j'appelais relire, puis je devais attendre EOS – Zombies

2

lecture jusqu'à ce que vous obtenez -1 signifie que vous voulez lire jusqu'à EOS. Si vous ne voulez pas lire jusqu'à EOS, ne bouclez pas avant le -1: arrêtez plus tôt. La question est "quand?" Si vous voulez lire un «message» complet et pas plus, vous devez envoyer le message de telle sorte que le lecteur puisse trouver sa fin: par exemple, un protocole type-length-value, ou plus simplement un mot de taille avant chaque message, ou un protocole auto-descriptif tel que XML.

1

le code fait probablement pas ce que vous pensez. Read (buffer) renvoie le nombre d'octets lus, en d'autres termes: il n'est pas garanti de remplir votre buffer de toute façon. Voir DataInputStream.readFully() pour le code qui remplissent le tableau entier:

ou vous pouvez utiliser cette fonction (qui sont basées sur DataInputStream.readFully()):

public final void readFully(InputStream in, byte b[]) throws IOException 
{ 
    readFully(in, b, 0, b.length); 
} 

public final void readFully(InputStream in, byte b[], int off, int len) throws IOException 
{ 
    if (len < 0) throw new IndexOutOfBoundsException(); 
    int n = 0; 
    while (n < len) 
    { 
     int count = in.read(b, off + n, len - n); 
     if (count < 0) throw new EOFException(); 
     n += count; 
    } 
} 

Votre code ressemblerait :

InputStream is = socket.getInputStream(); 
byte[] buffer = new byte[20000]; 
readFully(is, buffer); 
System.out.println("socket read");