Java,尝试根据命令长度创建特定的网络字节标头



我在尝试创建网络字节标头时遇到了一些麻烦。标头的长度应为 2 个字节,它只是定义以下命令的长度。

例如;以下命令 String"HED>0123456789ABCDEF"的长度为 20 个字符,0014为十六进制符号 2 补码,为此命令创建网络字节标头的工作方式是命令少于 124 个字符。以下代码片段实质上计算出字节标头,并在命令少于 124 个字符时将以下前缀添加到命令u00000u0014

但是,对于 124 个字符或以上的命令,if块中的代码不起作用。因此,我研究了可能的替代方案,并尝试了一些关于生成十六进制字符并将它们设置为网络字节标头的事情,但由于它们不是字节,因此无法正常工作(如else块所示(。相反,else块只是为一个长度为153个字符的命令返回0090,这在技术上是正确的,但我无法像if块长度标头那样使用此"长度"标头

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 个字符命令的以下网络字节标头作为x00x99

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]: 'x00x99'

如能提供任何援助或阐明这一问题,将不胜感激。

基本问题是你(尝试(将二进制数据转换为字符数据。 此外,您可以使用平台的默认字符集执行此操作,该字符集因计算机而异。

但是,我认为您稍微错误地描述了问题。 我相信当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();
}

最新更新