Сокет блокировки Java, возвращающий неполный ByteBuffer

У меня есть socketChannel, настроенный как блокирующий, но при чтении байтовых буферов размером 5 КБ из этого сокета я иногда получаю неполный буфер.

    ByteBuffer messageBody = ByteBuffer.allocate(5*1024);
    messageBody.mark();
    messageBody.order(ByteOrder.BIG_ENDIAN);
    int msgByteCount = channel.read(messageBody);

Иногда messageBody заполняется не полностью, и channel.read() возвращает не -1 или исключение, а фактическое количество прочитанных байтов (которое меньше 5k).

Кто-нибудь сталкивался с подобной проблемой?


person user1069275    schedule 26.04.2010    source источник


Ответы (3)


Так работает чтение. В документации SocketChannel говорится:

Операция чтения может не заполнить буфер и вообще не прочитать ни одного байта. [...] Однако гарантируется, что если канал находится в режиме блокировки и в буфере остался хотя бы один байт, то этот метод будет блокироваться до тех пор, пока не будет прочитан хотя бы один байт [ выделение добавлено].

person Anon    schedule 26.04.2010
comment
Читая спецификацию API, я ожидаю, что чтение не вернется до тех пор, пока не будет заполнен ByteBuffer (переданный на входе, а не базовый буфер сокета). Однако похоже, что это неправда. Я бы сказал, что реализация API сомнительна, поскольку клиент блокирующего сокета хочет, чтобы буфер был заполнен. Вот что происходит, если сокет полностью пуст, и я выполняю чтение. Чтение будет блокироваться до тех пор, пока что-то не прибудет. Так или иначе, обсуждение помогло мне решить проблему. Большое спасибо. - person user1069275; 27.04.2010
comment
Я не могу говорить о вашей интерпретации документов; по крайней мере, мне кажется очень недвусмысленным. И с практической точки зрения вы редко знаете, сколько данных будет поступать из сокета (в отличие, скажем, от файла), но вы хотите обрабатывать данные по мере их поступления, а не блокировать навсегда, если вы получаете меньше, чем ожидалось. - person Anon; 27.04.2010

Когда вы используете сокеты, вы должны предвидеть, что сокет может передавать меньше байтов, чем вы ожидаете. Вы должны зациклить метод .read, чтобы получить оставшиеся байты.

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

Такое поведение связано с тем, что сетевые уровни разбивают сообщения на несколько пакетов. Если ваши сообщения короткие, то вероятность того, что вы с этим столкнетесь, меньше. Но вы всегда должны кодировать его.

С 5k байтами на буфер вы, скорее всего, увидите, что сообщение отправителя разбито на несколько пакетов. Каждая операция чтения получит один пакет, который является только частью вашего сообщения.

person aaaa bbbb    schedule 26.04.2010
comment
Я думал, что блокирующий сокет предназначен для того, чтобы вам не приходилось зацикливаться, но это не на 100% верно. Я добавлю циклы в чтение/запись. Спасибо! - person user1069275; 27.04.2010

TCP/IP отправляет информацию в пакетах, они не всегда все доступны, когда вы выполняете чтение, поэтому вы должны выполнять чтение в цикле.

           char [] buffer = new char[1024];
           int chars_read;
           try
           {
              while((chars_read = from_server.read(buffer)) != -1)
              {
                 to_user.write(buffer,0,chars_read);
                 to_user.flush();
              }
           }
           catch(IOException e)
           {
              to_user.println(e);
           }

см. этот пост

person Romain Hippeau    schedule 26.04.2010
comment
Спасибо! Но вопрос в том, почему бы не добавить цикл в операцию записи? Я сделаю это, так как кажется, что блокирующие сокеты в любом случае не блокируются на 100%. - person user1069275; 27.04.2010
comment
@evandro-carrenho — Обновленный пост о том, почему вы должны читать в цикле - person Romain Hippeau; 27.04.2010