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

В целом, это происходит потому, что метод чтения пытается вернуть все данные, которые он может вам до блокировки, а не все данные, которые вы когда-либо получите.

Есть несколько методов, полезным я часто использую для такого рода вещи: (пропущено из контекста - обратите внимание, что я не являюсь автором метода 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 (read может вернуть 0 потому что новых данных нет, и мы можем закончить дренирование буфера, который у нас есть, оставив его в позиции 0), но было бы неправильно завершить цикл в это время (поскольку EOF не был достигнут) - чего я здесь не вижу ? – JVMATL

+0

Вам не хватает части после '|| .' – EJP

1

Вы предполагаете, что read() заполняет буфер. Проверьте Javadoc. Он передает хотя бы один байт, вот и все.

Вам не нужно как большой буфер и BufferedInputStream. Изменение последнего к DataInputStream.readFully().

Смежные вопросы