在Java中查找2个字节的值

3
我正在尝试查找UDP数据包中前两个字节的值,该值对应于剩余有效载荷的长度。如果我知道前两个字节,那么在Java中查找此值的最佳方法是什么?java.nio.ByteBuffer有用吗?
谢谢

你是在尝试从两个8位整数中获取一个16位整数吗? - zildjohn01
文档仅说明有效载荷长度在前两个字节中。可能只是一个16位整数,而不是添加两个8位整数。 - Trevor
如此简单的问题,却引发了一场史诗般的讨论。 - Justin
4个回答

5
我通常使用类似以下的代码:

我通常使用这样的代码:

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

然后你需要取出 DatagramPacket 的前两个字节:

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

请注意,我使用了长度作为int,因为在Java中,short数据类型(像所有人一样)也是有符号的,所以你需要更大的空间。

2
我更喜欢Justin和Erickson的风格,例如使用"<< 8"代替"* 256"和"|"代替"+",因为你所做的实际上是移位和合并位(这也可以解释为乘法和加法,但有点模糊意图)。顺便说一句,并不是所有的整数原始类型都是带符号的:char! - Dimitris Andreou
是的,运算符在性能上没有任何区别(因为JIT会自己通过位运算符进行优化)...这只是一个品味问题 :) - Jack
@Dimitris:嗯,我不同意:你真正做的是乘法和加法,将一个基于256编码的整数转换为Java整数。只是巧合的是编码基数是2的幂。当然,这并不是真正的幸运,而是出于效率考虑而这样做。所以我想我们两个都是正确的。 - President James K. Polk

3
使用ByteBuffer很方便,只是不要被Java有符号的16位值所困扰:
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;
  ⋮
}

如果您不想使用ByteBuffer,转换仍然相当容易。可以使用等价的乘法和加法,但是我经常看到使用位运算符:
int len = (data[0] & 0xFF) << 8 | data[1] & 0xFF;

2

你确实可以利用java.nio.ByteBuffer。以下是一个简单的示例:

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.

这确实使您能够在网络字节顺序和Windows字节顺序之间切换,但代价是对象分配(以及一些额外的逻辑)。 - Justin
1
另外,如果您正在使用2字节长度,则应该将其升级为int()以消除符号(((int)buffer.getShort(0)) | 0xFFFF)。 - Justin
@Justin: 你说得对。你希望最终得到无符号值。 - BalusC

1

如果你正在使用nio读取UDP数据包,那么使用ByteBuffer才有价值。你可以创建一个实用方法:

static final int getLength(DatagramPacket packet) {
 byte data[] = DatagramPacket.getData();
 return (int)((0xFF & (int)data[0]) << 8) | (0xFF & (int)data[1]));
}

ByteBuffer具有出色的支持,可以将字节数组解释为各种基本类型,包括大端和小端的支持。 - President James K. Polk
确实存在一个权衡:您正在将 ByteBuffer 的分配成本(以及随后的垃圾回收)与 data[0]、data[1] 的 2 次范围检查内存访问成本进行交换。除非您将 ByteBuffer 用作 DatagramChannel 读取的源,否则我认为这并不划算。 - Justin
所有的(int)强制转换都是多余的。否则,这是一种可行的方法。 - Alexander Pogrebnyak
1
人们对他们的答案非常固执。你的答案很好,除了第一句话很糟糕。使用ByteBuffer可能会慢一点,但本质上两者是相同的。在我看来,Java不是关于微观优化,而且“回报”不在于CPU周期,而在于程序员周期。你可能会认为你的解决方案比引入像ByteBuffer这样模糊的东西更清晰,这是一个公平的论点。我的经验是,ByteBuffers为将字节数组转换为各种Java int类型问题增加了统一性和清晰度。 - President James K. Polk
+1 鉴于固执的认识,尽管“程序员周期”可能有不同的含义:通过 ByteBuffer,我们引入了对 nio 的依赖,这意味着我们不是 1.4 可移植的,我们想要一个无符号的结果,但 ByteBuffer 并没有提供它,并且我们引入了一个仅用于一个目的(I/O 缓冲区)的类,实际上被用作读取一个 int 的实用方法。 - Justin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接