无符号短字节数组
Posted
技术标签:
【中文标题】无符号短字节数组【英文标题】:Unsigned short to byte array 【发布时间】:2015-04-30 17:10:28 【问题描述】:我必须将一个短作为无符号短发送到 TCPServer。
问题是 Java 不支持 usigned 短裤: 我尝试过的:
byte[] data = new byte[3];
short port = 5025;
data[0] = 1;
data[1] = (byte)(port & 0xff);
data[2] = (byte)((port >> 8) & 0xff);
这就是我在 C++ 中将数据转换为无符号 short 的方式
// 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] << 8
是 04<<8
并变成 0400。很好。 data[2]
是 80(-128),但它即将与 16 位数字组合,所以它变为 FF80(带符号的 16 位中的 -128)。 0400|FF80
=FF80(65408 无符号 16 位)。您必须修复 C++ 代码。它坏了。
【参考方案1】:
没关系。只需将其放在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[]
现在,如果您在ByteBuffer
中有更多.put*()
,请分配任何必要的空间等。
但是。您说您不想对您的 C++ 代码进行任何更改……这不能跨体系结构移植。如果您希望在 C++ 中通过网络读取/写入 16 位值,请使用 ntohs()/htons()
。
(也许有比这更好的 API;我已经有一段时间没有使用 C/C++ 进行高级网络编程了)
【讨论】:
再次阅读我的答案。特别是 C++ 部分。 但我不想更改 C++ 中的任何内容,因为另一个用 C++ 编写的客户端也在连接。 是的,您是否保证所有 C++ 客户端都将使用具有相同字节序的 CPU 架构?提示:您的 C++ 程序不适用于具有不同字节序的架构。 但是,Java 没有无符号原始数字类型这一事实根本不是问题所在;你的问题是字节序。尝试将 ByteBuffer 的字节序更改为小字节序(提示:这是 .order() 方法),但这样做基本上违反了万物联网的基本规则,该规则规定必须发送所有网络数据/以大端格式接收。ntoh*/hton*()
家族的定义并非一时兴起。
是的,我确定。在我想使用它的 PC 上,它可以工作。 :) 但我必须找到一个 java 解决方案【参考方案2】:
我的观点略有不同。 OP 正确使用位移来进入小端,因此 C++ 可移植性会很好,除非他正在处理非常规大小的字节。通信协议违背了网络上的大端约定,但有时支持遗留系统就是这样。
如果端口变量在提供的代码之外有用户,请使用 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 时,看起来您使用了一个字符,并且您的字节通过移位得到了符号扩展。这里有一些测试代码,你可以玩一下看看发生了什么。
#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]
这两个数字都必须按比例放大并进行签名,因此 0x80 变为 0xFF80。实际上,它们变成了整数,但那是另一回事了。
(0xFF80 << 8) | 0xFF80
简化为
0x8000 | 0xFF80
这就是 OR
0xFF80
AKA 65408,而不是 32896。
在这种情况下 unsigned char 是你的朋友。 Java 可能有问题,但 C++ 肯定坏了。
【讨论】:
谢谢!但我只收到端口 40975。如果我在 C++ 中打印 data[1] 和 data[2],我会得到以下信息:ffffffa1 13
C++ 中的数据数组是一种 char。是这个问题吗?
我已经把数组的typ改成了unsigned char,但是没有区别
哦,如果我在 java 中将字节设置为 0xa0 和 0x0f 一切正常。所以错误在java代码中
最好的判断方法是执行@fge 一直在询问的内容并编辑您的问题以包含更多或全部Java 输出例程和C++ 输入例程。没有它,我们就在黑暗中拍摄。您是否考虑过使用 Wireshark 或类似工具来查看数据包的内容以了解实际发送的内容?以上是关于无符号短字节数组的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Java 中的 BigInteger 获取无符号字节数组?
Python numpy'预期无符号字节数据类型的输入数组'
如何对实际上是 int[] 的无符号字节数组进行 base64 编码