SocketChannelImpl.write(ByteBuffer[] srcs, int offset, int length) appends ByteBuffer of size 0

467 views Asked by At

I have a client-server application communicating over Java NIO sockets which makes use of the SocketChannelImpl class underneath. When from the senders side, I send a ByteBuffer array of length n elements, the client always receives ByteBuffer array of length n+1 elements with the last ByteBuffer always being of the size 0.

I was wondering if that is some sort of an EOF indicator automatically sent by the SocketChannelImpl class to indicate a completed send of an array of ByteBuffers so that when the receiving side receives an array ByteBuffers followed by a BytBuffer of size 0 it knows that it has correctly received the ByteBuffer array that was sent from the senders side?

Update: Adding example to further elaborate on my question:

Whenever a call to SocketChannel.write(ByteBuffer[] srcs, int offset, int length) is made on the senders side and SocketChannel.read(ByteBuffer srcs) is made on the receivers side, I log the length of the array and the size of its element. Following is a set of logs from senders and receiver side:

Sender:

Writing using:write(ByteBuffer[] srcs, int offset, int length)
srcs.length == 2, totalBytesWritten = 1326

Receiver:

Read using read(ByteBuffer src)
totalBytesRead:4
totalBytesRead:1322
totalBytesRead:0

What I am asking is, why on the receiver side even though the amount of data that was sent by the sender(1326 bytes) is received by the client(4+1322), there is an extra call to the read(ByteBuffer src) is made which ends of reading 0 bytes. My (ill informed) guess was that was some kind of an EOF indicator, but from the comments on the question it looks like that has to do something with how the application itself goes about reading data from the channels.

2

There are 2 answers

0
user207421 On BEST ANSWER

SocketChannelImpl.write(ByteBuffer[] srcs, int offset, int length) appends ByteBuffer of size 0

No it doesn't.

I have a client-server application communicating over Java NIO sockets which makes use of the SocketChannelImpl class underneath.

Forget about what's underneath. You are using java.nio.channels.SocketChannel.

When from the senders side, I send a ByteBuffer array of length n elements

You don't. You send the contents. The sending ByteBuffer[] array is gathered into a byte stream and sent as bytes.

the client always receives ByteBuffer array of length n+1 elements with the last ByteBuffer always being of the size 0.

No it doesn't. The received bytes are scattered into another ByteBuffer[] array, if that's what you use to read the channel. If you use gather-write and scatter-read there is no relationship between the sending and receiving ByteBuffer arrays other than the actual bytes received.

I was wondering if that is some sort of an EOF indicator automatically sent by the SocketChannelImpl class to indicate a completed send of an array of ByteBuffers

No.

so that when the receiving side receives an array ByteBuffers

It doesn't. It receives a byte stream, into an array of ByteBuffers that it provided itself.

followed by a ByteBuffer of size 0 it knows that it has correctly received the ByteBuffer array that was sent from the senders side?

This doesn't begin to make sense. ByteBuffers are neither send nor received, either individually or as arrays.

Update: Adding example to further elaborate on my question:

There is no example here, only some output. An example would be useful but it would have to consist of Java code.

Whenever a call to SocketChannel.write(ByteBuffer[] srcs, int offset, int length) is made on the senders side and SocketChannel.read(ByteBuffer srcs)

Can we assume this should be SocketChannel.read(ByteBuffer[] srcs)? It doesn't make sense otherwise.

is made on the receivers side, I log the length of the array and the size of its element. Following is a set of logs from senders and receiver side:

Sender:

Writing using:write(ByteBuffer[] srcs, int offset, int length)
srcs.length == 2, totalBytesWritten = 1326

Receiver:

Read using read(ByteBuffer src)
totalBytesRead:4
totalBytesRead:1322
totalBytesRead:0

This can only mean two things:

  1. You used read(ByteBuffer src) and called it three times; the peer had only sent 1326 bytes; so the third read returned zero, meaning no data was available to be read.

OR

  1. You used read(ByteBuffer[] srcs), where srcs contained three ByteBuffers: one of length exactly 4, one of length >= 1322, and one of length > 0; only 1326 bytes were received, so the third ByteBuffer was cleared, or unaffected.

What I am asking is, why on the receiver side even though the amount of data that was sent by the sender(1326 bytes) is received by the client(4+1322), there is an extra call to the read(ByteBuffer src) is made which ends of reading 0 bytes.

There is no such call, unless you made it, and if you made it when there was no data available to be read you got zero bytes. Why is this surprising?

My (ill informed) guess was that was some kind of an EOF indicator

No. End of stream is signalled by read() returning -1.

but from the comments on the question it looks like that has to do something with how the application itself goes about reading data from the channels.

Correct. You tried to read beyond the end of the data that was sent, or with more ByteBuffers than were required to hold the data that was received. Simple as that.

1
Sergey On

Socket in OS (operating system) is a buffer with some limited capacity so if you write for example 10 Kb of the data that is not OS write this 10 Kb to the socket as the one chunk. OS for example write 10 chunk's by 1 Kb or another value, this is depends on the socket configuration in OS.

So we may read different number of the bytes at each read() method call. And the main thing in this that read() call depends on the type of the socket read method: Synchronous or Asynchronous. Difficult reading in asynchronous mode - if socket is empty at this time this is not means that we read the all data it's just means that in this time there are no data in channel but in the next read we can read for example 10 bytes and next 98 bytes and next 1098.

So in asynchronous mode we must send some bytes of the header metadata at the begining with for example data size we vill be transferring and the client will be know how many read() invocation it must call before data will be ended.