tags:

views:

304

answers:

2

I would like to read a string from a TCP stream that is given with a byte length followed by the actual data. In Python, I would do

length = ord(stream.read(1))
data = stream.read(length)

How do I do the same in Java NIO? What I have is a buffer (of capacity 257)

stream.read(buffer); // cannot specify a size here
int length = buffer.get();
byte[] data = new byte[length];
buffer.get(data);

Unfortunately, this doesn't work: the get() calls read past the data in the buffer :-(

I probably need some combination of flip, rewind, reset, etc., but I can't figure it out.

A: 

I suggest, you have a look into java.nio.channel.GatheringByteChannel and DatagramChannel. You'll need to specifiy a Datagram for your needs.

That's what you've done by your Python example (first byte indicates the payload length, read the playload using this length), which looks really fault-prone(!).

A datagram is a specification for a data stream. It divides your stream into packages, logically. So, a better strategy would be to send a payload-package first, that indicates the length of the payload (and the package size). Edit: Of course, both sides of the stream must speak the same protocol / datagram language.

oeogijjowefi
I fail to understand your recommendation. GatheringByteChannel is an interface that is already implemented by SocketChannel which I'm already using - AFAIU, that's the standard channel to use for TCP. DatagramChannel is irrelevant, as that's for UDP, and I'm doing TCP. I also fail to see why having a protocol that first sends the length and then the payload is fault-prone - that's how HTTP 1.1 works, for example.
Martin v. Löwis
I missed some details with your problem. Thought, you've more a problem with allocation than with ByteBuffer's methods ;) Anyway, If your payload does not exceed 255 byte, Bkkbrad's solution is straight forward.
oeogijjowefi
+5  A: 

if stream is a SocketChannel and buffer is a ByteBuffer, you can do the following:

//assuming buffer is a ByteBuffer
buffer.position(0);
buffer.limit(1);
while (buffer.hasRemaining()) { 
   stream.read(buffer);
}
buffer.rewind();

//get the byte and cast it into the range 0-255
int length = buffer.get() & 0xFF;
buffer.clear();
buffer.limit(length);
while (buffer.hasRemaining()) { 
   stream.read(buffer);
}
buffer.rewind();
//the buffer is now ready for reading the data from
Bkkbrad
Thanks, that should work fine - although it looks fairly complicated.
Martin v. Löwis
Verbose, not complicated.
Jason S