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

Java, trying to create a specific network byte header based on length of command

我在尝试创建网络字节 header 时遇到了一些麻烦 运行。 header 应该是 2 个字节长,它简单地定义了以下命令的长度。

例如;以下命令字符串 "HED>0123456789ABCDEF" 的长度为 20 个字符,即 0014 作为十六进制符号的 2 补码,为此命令创建网络字节 header 有效,因为该命令少于 124 个字符。下面的代码片段基本上计算出字节 header 并在命令少于 124 个字符时向命令 \u00000\u0014 添加以下前缀。

然而,对于 124 个字符或以上的命令,if 块中的代码不起作用。因此,我研究了可能的替代方案并尝试了一些关于生成十六进制字符并将它们设置为网络字节 header 的方法,但由于它们不是字节,因此无法正常工作(如 else块)。相反,else 块只是 returns 0090 用于 153 个字符长的命令,这在技术上是正确的,但我无法使用此 'length' header 与 if 块长度相同 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 中正常工作,不少!这 returns 以下网络字节 header 为 153 个字符的命令为 \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 时出现,因此包含 commandHeader 的长度的 commandLength 也至少为 128。您还会发现有一些(很多)更长的命令长度也有效。

这里的关键点是,当长度的二进制表示中的任何字节设置了最高有效位时,这对某些字符编码有意义,尤其是 UTF-8,这是一种常见的(但不通用)默认。除非您非常幸运,否则具有任何此类字节的二进制长度将不会被正确解码为 UTF-8 中的字符。此外,它们可能会成功解码为字符,但在为此目的使用不同字符集的机器上会有所不同。

您还有另一个相关的不一致之处。您正在格式化数据以通过网络传输,这是一种面向字节的媒体。传输将是一个字节序列。但是您正在测量和报告解码内部表示中 characters 的数量,而不是将经过的编码表示中的 bytes 数量金属丝。这两个计数对于您的示例命令是相同的,但对于您可以在 Java.

中表达的某些字符串,它们会有所不同

此外,您的代码与您对所需格式的描述不一致。你说 "network byte header" 应该是四个字节长,但你的代码只发出两个字节。

您可以通过明确考虑字符编码并避免将原始二进制数据不必要和不适当地转换为字符数据来解决所有这些问题。您已经在使用的 ByteBuffer class 可以提供帮助。例如:

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();
}