tags:

views:

232

answers:

3

I have a socketChannel configured as blocking, but when reading byte buffers of 5K from this socket, I get an incomplete buffer sometimes.

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

Ocasionally, messageBody is not completely filled and channel.read() does not return -1 or an exception, but the actual number of bytes read (which is less than 5k).

Has anyone experienced a similar problem?

+3  A: 

That's how reads work. The SocketChannel documentation says:

A read operation might not fill the buffer, and in fact it might not read any bytes at all. [...] It is guaranteed, however, that if a channel is in blocking mode and there is at least one byte remaining in the buffer then this method will block until at least one byte is read [emphasis added].

Anon
By reading the API specification, I would expect that the read does not return until the ByteBuffer (passed in the input not the underlying socket buffer) is filled. However, it seems that is not true. I would say the API implementation is questionable, as a blocking socket client wants the buffer to be filled.This is what happens if the socket is completly empty and I perform a read. The read will block until something arrives.Anyway, the discussion helped me solving the issue. Thanks a lot.
I can't speak to your interpretation of the docs; "at least" seems very unambiguous to me. And from a practical perspective, you rarely know how much data is going to come from a socket (unlike, say, a file), yet you want to process data as it arrives, not block forever if you get less than expected.
Anon
+1  A: 

When you use sockets you must anticipate that the socket might transfer fewer bytes than you expect. You must loop on the .read method to get the remainder of the bytes.

This is also true when you send bytes through a socket. You must check how many bytes were sent, and loop on the send until all bytes have been sent.

This behavior is due to the network layers splitting the messages into multiple packets. If your messages are short, then you are less likely to encounter this. But you should always code for it.

With 5k bytes per buffer you are very likely to see the sender's message spit into multiple packets. Each read operation will receive one packet, which is only part of your message.

aaaa bbbb
I thought that blocking socket are meant to avoid you having to loop, but that is not 100% true. I will add loops in the read/write. Thanks!
A: 

TCP/IP sends the information in packets, they are not always all available when you do the read, therefore you must do the read in a loop.

           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);
           }

See this post

Romain Hippeau
Thanks! But the question is why not adding the loop in the write operation? I will do it as it seems that blocking sockets are not 100% blocking anyways.
@evandro-carrenho - Updated post as to why you must read in a loop
Romain Hippeau