2014-01-13 14 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); 

を一度(読み取り)bytesReadcontentLengthに等しい時々終了が、他の機会にされていますread()はコンテンツの最後までは読んでいないようです。 誰が何が起こっているかについてのアイデアはありますか? Javaバッファリングは出力されますか?ソケットから読む方がいいですか?

答えて

0

これは、read()メソッドの通常の動作です。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

に大きなバッファ、後者BufferedInputStream.変更の両方を必要としません。 ()> 0)。最後に、EOFで終わるものを取り除くことができます。 – EJP

+0

こんにちは@ejp、コメントありがとうございました。あなたはおそらく正しいと思いますが、私はそれほど得意ではありません - あなたは詳細を教えてください。 'while(src.read(buffer)!= -1)'をあなたの提案と置き換えた場合、たとえば、http転送が一時的に停止した場合(readは0を返す可能性がある)新しいデータが利用できないので、私たちが持っているバッファを排水し終えて、位置0にしておくことができます)。しかし、この時点でループを終了させるのは間違いでしょう(EOFに達していないためです)。 ? – JVMATL

+0

あなたは '|| 'の後ろの部分を見逃しています – EJP

1

read()がバッファをいっぱいにしているとします。 Javadocを確認してください。それは少なくとも1バイトを転送します、それはそれだけです。

あなたは `channelCopy()`メソッドのループは(src.read(バッファ)> 0 || buffer.positionながら `でなければなりませんDataInputStream.readFully().

関連する問題