tags:

views:

162

answers:

4

I am trying to find the value of the first 2 bytes in a UDP packet which corresponds to the length of the remaining payload. What is the best method to find this value in Java given that I know the first 2 bytes? Would java.nio.ByteBuffer be of any use?

Thanks

+5  A: 

I usually use something like this:

static public int buildShort(byte high, byte low)
{
  return ((0xFF & (int) high) * 256) + ((0xFF & (int) low));
}

Then you take first two bytes of your DatagramPacket:

int length = buildShort(packet.getData()[0], packet.getData()[1]);

Mind that I used length as an int because also short data type (as everyone) is signed in Java, so you need a larger space.

Jack
I prefer Justin's and erickson's style, e.g. "<< 8" instead of "* 256" and "|" instead of "+", since what you are doing is really shifting and combining bits (which can also be interpreted as multiplication and addition, but obscuring a bit the intention).By the way, not _all_ integral primitive types are signed: char!
Dimitris Andreou
Yeah, operators don't involve any difference in performance (since JIT will do the dirty work of optimizing this with bitwise operators by itself).. it's just a matter of taste :)
Jack
@Dimitris: Well, I disagree: what you are *really* doing is the multiplication and addition to convert a base-256 encoded integer into a java integer. It is just lucky coincidence that the encoding base is a power of two. Well, off course not really lucky, it is done that way for the efficiency. So I guess we're both correct.
GregS
+1  A: 

Using ByteBuffer would only be of value if you are reading the UDP packets (using nio). You can create a utility method:

static final int getLength(DatagramPacket packet) {
 byte data[] = DatagramPacket.getData();
 return (int)((0xFF & (int)data[0]) << 8) | (0xFF & (int)data[1]));
}
Justin
ByteBuffer's have excellent support for interpreting byte arrays as various kinds of primitive types, including support for big and little endian.
GregS
True, there is a trade-off here: you are trading the allocation costs (and subsequent garbage collection) of the ByteBuffer against the cost of 2 range checked memory access for data[0], data[1]. Unless you use the ByteBuffer as the source for the DatagramChannel to read into, I don't think it pays off.
Justin
All `(int)` casts are redundant. Otherwise, it's a way to go.
Alexander Pogrebnyak
People are amazingly stubborn about their answers. Your answer is great except the first sentence, which is terrible. It's true that using a ByteBuffer will probably a tiny bit slower, but both are essentially the same. IMHO, Java is not about micro-optimization, and the "pay off" is not in CPU cycles it is in programmer cycles. You might argue that your solution is more clear then bringing in something as obscure as a ByteBuffer, a fair argument. My experience is that ByteBuffers add uniformity and clarity to the problem of converting byte arrays into various kinds of java ints.
GregS
+1 for recognizing stubbornness, though 'programmer cycles' can have various meaning: with ByteBuffer we have introduced a dependency on nio, which means we are not 1.4 portable, we want an unsigned result, but ByteBuffer doesn't offer it, and we introduced a class used for one purpose (an I/O buffer) which is really being used as a utility method for reading one int.
Justin
+2  A: 

You can indeed make use of java.nio.ByteBuffer. Here's a kickoff example:

ByteBuffer buffer = ByteBuffer.allocate(2);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.put(byte1);
buffer.put(byte2);
int length = buffer.getShort(0) & 0xFFFF; // Get rid of sign.
BalusC
This does get you the ability to switch between network and windows byte order, but at the expense of an object allocation (and some extra logic)
Justin
Also, if you are using 2 byte length, you should up convert to int() to get rid of the sign (`((int)buffer.getShort(0)) | 0xFFFF`)
Justin
@Justin: you're right. You would like to end up with unsigned value.
BalusC
Its really a shame there is no ByteBuffer.readUnsignedXXX()
Justin
Alexander Pogrebnyak
+3  A: 

Using a ByteBuffer is convenient, just don't get tripped up by Java signed 16-bit values:

byte[] data = new byte[MAX_LEN];
ByteBuffer buf = ByteBuffer.wrap(data);
DatagramPacket pkt = new DatagramPacket(data, data.length);
⋮    
while (connected) {
  socket.receive(pkt);
  int len = buf.getShort() & 0xFFFF;
  ⋮
}

If you don't want to use ByteBuffer, the conversion is still fairly easy. The equivalent multiplication and addition can be used, but I see bit operators used more frequently:

int len = (data[0] & 0xFF) << 8 | data[1] & 0xFF;
erickson