DataInputStream.read возвращает меньше чем len

Я использую DataInputStream для чтения некоторых байтов из сокета. У меня есть ожидаемое количество байтов для чтения из потока (после декодирования заголовка я знаю, сколько байтов содержится в сообщении). Это работает в 99% случаев, но иногда я получаю количество прочитанных байтов меньше, чем len.

int numRead = dis.read(buffer, 0, len);

Что может стать причиной того, что numRead будет меньше, чем len? Это не -1. Я ожидал бы, что поведение чтения будет блокироваться, пока поток не будет закрыт или пока не будет достигнут EOF, но если это сокет, лежащий в основе потоков, этого не должно произойти, если сокет не закроется, верно?

Есть ли способ чтения байтов из сокета, который всегда гарантирует, что вы читаете len байтов?

Спасибо

4 ответа

РЕДАКТИРОВАТЬ: Для общего потока, вы просто продолжаете читать, пока вы не прочитаете все, что вы хотите, в основном. Для реализации DataInput (такие как DataInputStream) вы должны использовать readFully, как предположил Питер Лори. Считайте, что остальная часть этого ответа важна для общего случая, когда у вас просто есть InputStream,

Это вполне разумно для InputStream любого типа, чтобы дать вам меньше данных, чем вы просили, даже если больше в пути. Вы должны всегда кодировать эту возможность - с возможным исключением ByteArrayInputStream, (Конечно, это может вернуть меньше данных, чем было запрошено, если осталось меньше данных, чем вы просили.)

Вот такая петля, о которой я говорю:

byte[] data = new byte[messageSize];
int totalRead = 0;

while (totalRead < messageSize) {
    int bytesRead = stream.read(data, totalRead, messageSize - totalRead);
    if (bytesRead < 0) {
        // Change behaviour if this isn't an error condition
        throw new IOException("Data stream ended prematurely");
    }
    totalRead += bytesRead;
}

Вы можете использовать DataInputStream таким образом.

byte[] bytes = new byte[len];
dis.readFully(bytes);

Это либо вернет все прочитанные данные, либо выдаст IOException.

Чтение возвращает каждый раз с битами, которые были доступны в то время, и -1, когда вы закончите, вы обычно должны делать

while (true) {
  int numRead = dis.read(buffer, 0, len);
  if (numRead == -1) break;
  total.append(buffer, numRead);
}

Есть ли способ чтения байтов из сокета, который всегда гарантирует, что вы прочитаете len байтов?

Вы можете использовать Apache Commons IOUtils, у них есть метод, который делает именно то, что вам нужно:

    byte[] buf = new byte[BUFFER_SIZE];
    int length;

    do {
        length = IOUtils.read(inputStream, buf);
        if (length > 0) {
            //do something with buf
        }
    } while (length == BUFFER_SIZE);

Я ожидал бы, что поведение чтения будет блокироваться, пока поток не будет закрыт или не будет достигнут EOF.

Тогда вам нужно проверить Javadocs. Контракт read() заключается в том, что он будет читать по крайней мере один байт, блокируя при необходимости, пока это не будет сделано, или пока не произойдет EOS или исключение. В спецификации ничего не сказано о том, что он будет читать всю запрашиваемую вами длину. Вот почему он возвращает длину.

Другие вопросы по тегам