我在尝试创建网络字节头时遇到了一些麻烦。 header 长度应为 2 个字节,它仅定义以下命令的长度。
例如;以下命令字符串 "HED>0123456789ABCDEF"
长度为 20 个字符,即 0014
作为十六进制符号 2 补码,为此命令创建网络字节头的工作原理如下少于 124 个字符。以下代码片段本质上计算出字节 header ,并在命令少于 124 个字符时将以下前缀添加到命令 \u00000\u0014
中。
但是,对于 124 个字符或以上的命令,if
block 中的代码不起作用。因此,我研究了可能的替代方案,并尝试了一些有关生成十六进制字符并将它们设置为网络字节 header 的方法,但由于它们不是字节,因此无法工作(如 else
中所示) > block )。相反,else
block 只是简单地返回 0090
来表示长度为 153
个字符的命令,这在技术上是正确的,但我无法使用它'length' header 与 if
block 长度 header 的方式相同
public static void main(String[] args) {
final String commandHeader = "HED>";
final String command = "0123456789ABCDEF";
short commandLength = (short) (commandHeader.length() + command.length());
char[] array;
if( commandLength < 124 )
{
final ByteBuffer bb = ByteBuffer.allocate(2).putShort(commandLength);
array = new String( bb.array() ).toCharArray();
}
else
{
final ByteBuffer bb = ByteBuffer.allocate(2).putShort(commandLength);
array = convertToHex(bb.array());
}
final String command = new String(array) + commandHeader + command;
System.out.println( command );
}
private static char[] convertToHex(byte[] data) {
final StringBuilder buf = new StringBuilder();
for (byte b : data) {
int halfByte = (b >>> 4) & 0x0F;
int twoHalves = 0;
do {
if ((0 <= halfByte) && (halfByte <= 9))
buf.append((char) ( '0' + halfByte));
halfByte = b & 0x0F;
} while (twoHalves++ < 1);
}
return buf.toString().toCharArray();
}
此外,我通过执行以下三行代码,成功地在 Python 2 中实现了此功能!这将返回 153 个字符命令的以下网络字节 header :\x00\x99
msg_length = len(str_header + str_command)
command_length = pack('>h', msg_length)
command = command_length + str_header + str_command
也可以通过运行 Python 2 并输入以下命令来简单地复制:
In [1]: import struct
In [2]: struct.pack('>h', 153)
Out[2]: '\x00\x99'
任何可以解决此问题的帮助或启示将不胜感激。
最佳答案
基本问题是您(尝试)将基本的二进制数据转换为字符数据。此外,您可以使用平台的默认字符集来执行此操作,该字符集因机器而异。
但是,我认为您对问题的描述有些错误。我确信当 command.length()
至少为 124 时就会出现这种情况,因此 commandLength
也包括 commandHeader
的长度,至少是 128。您还会发现有一些(大得多)的命令长度也有效。
这里的关键点是,当长度的二进制表示中的任何字节都有其最高有效位设置时,这对某些字符编码有意义,尤其是 UTF-8,这是一种常见(但不是通用)的编码。 ) 默认。除非您非常幸运,否则具有任何此类字节的二进制长度将无法正确解码为 UTF-8 中的字符。此外,它们可能会成功解码为字符,但在使用不同字符集的机器上会有所不同。
您还有另一个相关的不一致之处。您正在格式化数据以便通过网络(一种面向字节的介质)传输。传输将是一个字节序列。但是您测量和报告的是解码的内部表示中的字符数,而不是通过网络传输的编码表示中的字节数。对于您的示例命令,这两个计数是相同的,但对于您可以用 Java 表达的某些字符串,它们会有所不同。
此外,您的代码与您对所需格式的描述不一致。您说“网络字节头”应该是四个字节长,但您的代码只发出两个字节。
您可以通过明确考虑字符编码并避免将原始二进制数据不必要且不适本地转换为字符数据来解决所有这些问题。您已经使用的 ByteBuffer 类可以帮助解决这个问题。例如:
public static void main(String[] args) throws IOException {
String commandHeader = "HED>";
// a 128-byte command
String command = "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF"
+ "0123456789ABCDEF";
// Convert characters to bytes, and do so with a specified charset
// Note that ALL Java implementations are required to support UTF-8
byte[] commandHeaderBytes = commandHeader.getBytes("UTF-8");
byte[] commandBytes = command.getBytes("UTF-8");
// Measure the command length in bytes, since that's what the receiver
// will need to know
int commandLength = commandHeaderBytes.length + commandBytes.length;
// Build the whole message in your ByteBuffer
// Allow a 4-byte length field, per spec
ByteBuffer bb = ByteBuffer.allocate(commandLength + 4);
bb.putInt(commandLength)
.put(commandHeaderBytes)
.put(commandBytes);
// DO NOT convert to a String or other character type. Output the
// bytes directly.
System.out.write(bb.array());
System.out.println();
}
关于Java,尝试根据命令长度创建特定的网络字节头,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51793424/