无符号短整型转字节数组

4

我需要将一个short转换为无符号short并发送给TCPServer。

问题在于Java不支持无符号short类型:

我尝试过以下方法:

byte[] data = new byte[3];
short port = 5025;

data[0] = 1;
data[1] = (byte)(port & 0xff);
data[2] = (byte)((port >> 8) & 0xff);

以下是我在C++中将数据转换为无符号短整型的方法:

// Bytes to Short (uint16)
unsigned short port = (data[1] << 8) | data[2];

那么我该如何在Java中解决这个问题?(我不想更改C++代码)

编辑:// 我的新Java代码:

byte[] data = new byte[3];
short port = 1151; // short or int doesn't matter in this case

ByteBuffer buffer = ByteBuffer.allocate(5);
buffer.put((byte) 1);
buffer.putShort(port);

out.write(buffer.array());

C++代码:(相同)
unsigned short port = (data[1] << 8) | data[2];

如果端口号在0-1151和16384-32767之间,我可以获得正确的端口号,但为什么其余的端口号不起作用呢?


你需要了解数字的工作原理。1151 = 0x047f。将其分成两个字节04和7f。两者都是正数。1152 = 0x0480。04和80。80是负数(-128),char是有符号变量,因此如果调整大小,负数将被保留。data[1] << 804<<8,变成0400。data[2]是80(-128),但它即将与16位数字组合,因此它变成FF80(在有符号16位中为-128)。0400|FF80=FF80(在无符号16位中为65408)。你必须修复C++代码。它已经损坏了。 - user4581301
2个回答

3

没关系,把它放在一个 short 里就可以了。不用担心short是有符号的,因为short仍然是16位。

重要的是在这里要注意字节序。如果您在网络上传输数据,则应使用大端字节序。

这是 ByteBuffer 的默认设置,也是Java中所有数字基本类型的默认设置。

那么,该怎么做呢?对于您的特定示例,请使用以下方法:

// Just for a short...
final ByteBuffer buf = ByteBuffer.allocate(3);
buf.put((byte) 1);
buf.putShort(myShort);
final byte[] contents = buf.array();
// send the byte[]

现在,如果您需要用.put*()填充更多的内容到ByteBuffer中,请分配必要的空间等。
然而,您说您不想改变C++代码...这是跨架构不可移植的。如果您希望在C++中读写16位值以通过网络传输,请使用ntohs()/htons()
(也许有比此更好的API;我已经很长时间没有进行高级C/C++网络编程了)

请再次阅读我的回答,特别是 C++ 部分。 - fge
但我不想改变C++中的任何内容,因为另一个客户端是用C++编写的,也在连接。 - ForJ9
但是再次强调:Java没有无符号原始数值类型并不是问题的关键;你的问题在于字节序。尝试将ByteBuffer的字节序更改为小端序(提示:使用.order()方法),但这样做基本上违反了所有网络数据必须以大端序发送/接收的基本规则。ntoh*/hton*()系列函数不是凭空定义的。 - fge
如果我使用little_endian,它就不能正常工作。我得不到正确的结果。 - ForJ9
那并没有说明任何问题。请展示C++中的发送和接收代码以及Java中的发送/接收代码。 - fge
显示剩余2条评论

1
我有一个稍微不同的观点。OP 正确使用位移来进入小端模式,所以 C++ 的可移植性会很好,除非他正在处理非常规大小的字节。通信协议违反了大端网络约定,但有时支持旧系统就是这样。
如果 port 变量在提供的代码之外有用户,请使用 int 并仅发送您在上面的 Java 示例中想要的位。如果您正在传递该端口,则不断调整该死的符号位非常麻烦,迟早会出错。如果没有其他人需要使用端口,则符号将无关紧要。
byte[] data = new byte[3];
int port = 5025; // short or int doesn't matter in this case

data[0] = 1;
data[1] = (byte)(port & 0xff);
data[2] = (byte)((port >> 8) & 0xff);

当读取并得到65440时,看起来你使用了char类型,而且通过移位操作进行了符号扩展。以下是一些测试代码,可以让你尝试并了解发生了什么。

#include <cstdio>

int main()
{
    unsigned short val = 32896;
    char hi = (char)((val >> 8) & 0xFF);
    char lo = (char)(val &0xFF);
    printf("Watch what the sign bit can do to the bytes here:\n");
    printf("Value: %d, raw in hex: %04x, Hi byte: %02x, Low byte: %02x\n", val, val, hi, lo);



    printf("This one only works if the low byte doesn't sign extend\n");
    char datas[3] = {0, hi, lo};
    unsigned short port = (datas[1] << 8) | datas[2];
    printf("Reassembled short: %u, In Hex: %04x\n", port, port);

    printf("This one works, but will not for an integer\n");
    port = (datas[1] << 8) | (datas[2] & 0xFF);
    printf("Reassembled short: %u, in Hex: %04x\n", port, port);
    unsigned int bigport = (datas[1] << 8) | (datas[2] & 0xFF);
    printf("Reassembled int: %u, in Hex: %04x\n", bigport, bigport);

    printf("With unsigned characters it just works\n");
    unsigned char datau[3] = {0, hi, lo};
    port = (datau[1] << 8) | datau[2];
    printf("Reassembled short: %u, In Hex: %04x\n", port, port);
    bigport = (datau[1] << 8) | (datau[2] & 0xFF);
    printf("Reassembled int: %u, in Hex: %04x\n", bigport, bigport);
}

输出:

Watch what the sign bit can do to the bytes here:
Value: 32896, raw in hex: 8080, Hi byte: ffffff80, Low byte: ffffff80
This one only works if the low byte doesn't sign extend
Reassembled short: 65408, In Hex: ff80
This one works, but will not for an integer
Reassembled short: 32896, in Hex: 8080
Reassembled int: 4294934656, in Hex: ffff8080
This one just works
Reassembled short: 32896, In Hex: 8080
Reassembled int: 32896, in Hex: 8080

所以发生了什么?
(datas[1] << 8) | datas[2]

两个数字必须缩放到short并且是有符号的,因此0x80变成了0xFF80。实际上它们变成了整数,但这是另一个故事。

(0xFF80 << 8) | 0xFF80

简化为
0x8000 | 0xFF80

并且 进行逻辑或运算,得到

0xFF80

AKA 65408,而不是32896。

在这种情况下,无符号字符是您的好朋友。Java可能存在问题,但C++肯定有问题。


谢谢!但我只收到了端口40975。如果在C++中打印data[1]和data[2],我得到以下结果:ffffffa1 13 。在C ++中,数据数组是char类型的。那是问题所在吗? - ForJ9
我已经将数组类型更改为unsigned char,但没有任何区别。 - ForJ9
哦,如果我在Java中将字节设置为0xa0和0x0f,一切都可以正常工作。因此错误在Java代码中。 - ForJ9
最好的方法是按照@fge一直在问的做法,编辑您的问题以包括更多或全部Java输出例程和C++输入例程。如果没有这些,我们就是在瞎猜。您是否考虑使用Wireshark或类似工具查看数据包的内容,以了解实际发送的内容? - user4581301

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