2014-01-13 40 views
5

我使用Java的BufferedInputStream類讀取發送到套接字的字節。 到套接字的數據是HTTP形式,因此通常是具有定義的內容長度的頭,然後是一些內容。使用BufferedInputStream讀取套接字

我遇到的問題是,有時BufferedInputStream.read()不會讀取發送給它的全部數據量。它返回讀取的字節數,但是這比已經發送的少得多。我已經驗證使用Wireshark發送的字節數和正在傳輸可以確認完整的消息)

下面

示例代碼:

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

一旦讀()有時bytesRead完成等於contentLength但在其他場合read()似乎沒有讀到內容的末尾。 有沒有人有什麼想法發生了什麼? Java緩衝輸出是什麼?是否有更好的方法從套接字讀取?

回答

0

這是read()方法的正常行爲:您需要繼續讀取循環,直到讀取返回-1。 (參見http://docs.oracle.com/javase/7/docs/api/java/io/BufferedInputStream.html#read(byte[],%20int,%20int))

一般來說,它發生是因爲read方法試圖在阻塞之前返回它可以給你的所有數據,而不是你將獲得的所有數據。

有幾個實用方法我經常使用這樣的事情:(剪斷斷章取義 - 注意,我不是channelCopy方法的作者,但源歸因)

/** 
    * 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

的'channelCopy()'方法的循環應該是'而(src.read(緩衝)> 0 || buffer.position ()> 0)。「然後你可以擺脫在EOF結束的那些東西。 – EJP

+0

嗨@ejp,感謝您的評論;我希望你可能是對的,但我不太明白 - 你能否詳細說明一下?如果我用你的建議替換'while(src.read(buffer)!= -1)',在我看來複制操作可能會提前終止,例如,如果http傳輸暫時停止(讀取可能返回0因爲沒有新的數據可用,並且我們可以完成耗盡我們有的緩衝區,將它留在位置0,)但是此時終止循環是錯誤的(因爲EOF尚未到達) - 我在這裏錯過了什麼? – JVMATL

+0

你錯過了'||。'後面的部分 – EJP

1

你假設read()填充緩衝區。檢查Javadoc。它傳輸至少一個字節,就是這麼說的。

你並不需要這兩個大的緩衝和BufferedInputStream.更改後者DataInputStream.readFully().