2014-01-13 9 views
5

Ich verwende Javas BufferedInputStream Klasse, um Bytes an einen Socket gesendet zu lesen. Die Daten an den Socket ist HTTP-Formular, so im Allgemeinen ist ein Header mit definierten Content-Länge, dann einige Inhalte.Socket lesen mit BufferedInputStream

Das Problem, das ich habe, ist, dass manchmal BufferedInputStream.read() wird nicht die gesamte Menge der Daten an es gesendet. Es gibt die Anzahl der gelesenen Bytes zurück, aber das ist viel weniger als gesendet wurde. Ich habe den gesendeten Bytes mit Wireshark überprüft und kann die vollständige Meldung bestätigen wird übertragen)

Beispielcode unten.

BufferedInputStream inFromClient = new BufferedInputStream(socket.getInputStream()); 
int contentLength = getContentLengthFromHeader();  
byte[] b = new byte[contentLength]; 
int bytesRead = inFromClient.read(b, 0, contentLength); 

einmal gelesen() manchmal bytesRead beendet ist gleich contentLength ist aber auch bei anderen Gelegenheiten read() scheint nicht bis zum Ende des Inhalts zu lesen. Hat jemand irgendwelche Ideen, was passiert? Ist Java-Pufferausgabe? Gibt es bessere Möglichkeiten, aus Sockeln zu lesen?

Antwort

0

Das ist normales Verhalten für die read() - Methode: Sie müssen in einer Schleife lesen, bis lesen -1 zurückgibt. (siehe http://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html#read(byte[],%20int,%20int))

Im Allgemeinen ist es passiert, weil die read-Methode versucht, alle möglichen Daten an Sie zurück zu geben, bevor sie blockiert, nicht alle Daten, die Sie jemals bekommen werden.

Es gibt ein paar nützlichen Methoden für diese Art der Sache, die ich häufig verwenden: (aus dem Zusammenhang snipped - beachten Sie, dass ich nicht der Autor der channelCopy Methode, aber die Quelle zugeschrieben wird)

/** 
    * Efficiently copy from an InputStream to an OutputStream; uses channels and 
    * direct buffering for a faster copy than oldCopy. 
    * @param in - non-null readable inputstream 
    * @param out - non-null writeable outputstream 
    * @throws IOException if unable to read or write for some reason. 
    */ 
    public static void streamCopy(InputStream in, OutputStream out) throws IOException { 
     assert (in != null); 
     assert (out != null); 
     ReadableByteChannel inChannel = Channels.newChannel(in); 
     WritableByteChannel outChannel = Channels.newChannel(out); 
     channelCopy(inChannel, outChannel); 
    } 

    /** 
    * Read the *BINARY* data from an InputStream into an array of bytes. Don't 
    * use this for text. 
    * @param is - non-null InputStream 
    * @return a byte array with the all the bytes provided by the InputStream 
    * until it reaches EOF. 
    * @throws IOException 
    */ 
    public static byte[] getBytes(InputStream is) throws IOException{ 
     ByteArrayOutputStream os = new ByteArrayOutputStream(); 
     streamCopy(is, os); 
     return os.toByteArray(); 
    } 


    /** 
    * A fast method to copy bytes from one channel to another; uses direct 16k 
    * buffers to minimize copies and OS overhead. 
    * @author http://thomaswabner.wordpress.com/2007/10/09/fast-stream-copy-using-javanio-channels/ 
    * @param src - a non-null readable bytechannel to read the data from 
    * @param dest - a non-null writeable byte channel to write the data to 
    */ 
    public static void channelCopy(final ReadableByteChannel src, final WritableByteChannel dest) throws IOException { 
     assert (src != null); 
     assert (dest != null); 
     final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024); 
     while (src.read(buffer) != -1) { 
     // prepare the buffer to be drained 
     buffer.flip(); 
     // write to the channel, may block 
     dest.write(buffer); 
     // If partial transfer, shift remainder down 
     // If buffer is empty, same as doing clear() 
     buffer.compact(); 
     } 

     // EOF will leave buffer in fill state 
     buffer.flip(); 

     // make sure the buffer is fully drained. 
     while (buffer.hasRemaining()) { 
     dest.write(buffer); 
     } 
    } 
+0

Die 'channelCopy()' Methode der Schleife sollte 'while (src.read (Puffer)> 0 || buffer.position()> 0) .' Sie können dann das Zeug am Ende, das bei EOF endet, loswerden. – EJP

+0

Hallo @ejp, danke fürs kommentieren; Ich nehme an, du hast wahrscheinlich Recht, aber ich verstehe es nicht ganz - kannst du bitte etwas ausarbeiten? Wenn ich das 'while (src.read (buffer)! = -1)' mit dem, was Sie vorschlagen, ersetzen, scheint es mir, dass der Kopiervorgang vorzeitig beendet wird, wenn zum Beispiel eine HTTP-Übertragung vorübergehend anhält (read könnte 0 zurückgeben weil keine neuen Daten verfügbar sind, und wir könnten den Puffer leeren, den wir haben, und ihn auf Position 0 belassen, aber es wäre falsch, die Schleife zu diesem Zeitpunkt zu beenden (weil EOF nicht erreicht wurde) - was fehlt mir hier ? – JVMATL

+0

Sie vermissen den Teil nach dem '|| .' – EJP

1

Sie gehen davon aus, dass read() den Puffer füllt. Überprüfen Sie das Javadoc. Es überträgt mindestens ein Byte, das ist alles, was es sagt.

Sie brauchen keinen beide großen Puffer und einen BufferedInputStream. ändern letzteres DataInputStream.readFully().