【问题标题】:Java, trying to create a specific network byte header based on length of commandJava,尝试根据命令长度创建特定的网络字节头
【发布时间】:2018-08-10 20:06:43
【问题描述】:

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

例如;以下命令字符串"HED>0123456789ABCDEF" 长度为 20 个字符,即0014 为十六进制带符号的 2 补码,为该命令创建网络字节头的工作原理是该命令少于 124 个字符。下面的 sn-p 代码本质上是计算出字节头,并在命令少于 124 个字符时将以下前缀添加到命令 \u00000\u0014

但是对于 124 个字符或以上的命令,if 块中的代码不起作用。因此,我研究了可能的替代方案,并尝试了一些关于生成十六进制字符并将它们设置为网络字节头的方法,但由于它们不是字节,所以它不起作用(如 else 块中所示)。相反,else 块仅返回 0090 以获取 153 字符长的命令,这在技术上是正确的,但我无法像 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 个字符的命令返回以下网络字节标头 \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'

非常感谢任何可以解决此问题的帮助或提示。

【问题讨论】:

  • 您的消息本质上是一个 byte 序列。为什么要将其转换为字符序列? (在您的特定情况下为String,但char[]StringBuilderReader 不会更好。)
  • 我们是否应该评估 Java 代码,就好像它没有明确区分 if( commandLength &lt; 124 ) 和替代代码一样?
  • @JohnBollinger 对不起,如果我没有说清楚,我已经编辑了 sn-ps 以更好地反映 Java 代码,并稍微更新了描述

标签: java python sockets byte short


【解决方案1】:

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

但是,我认为您对问题的描述略有误解。我相信它会在command.length() 至少为 124 时出现,因此 commandLength(包括 commandHeader 的长度)也至少为 128。您还会发现有一些(很多)更大的命令长度也有效。

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

您还有另一个相关的不一致之处。您正在格式化数据以通过网络传输,这是一种面向字节的介质。传输将是一个字节序列。但是您正在测量和报告解码的内部表示中的 characters 的数量,而不是通过线路传输的编码表示中的 bytes 的数量。对于您的示例命令,这两个计数是相同的,但对于您可以用 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();
}

【讨论】:

  • 嗨,约翰,抱歉。当我写这个问题时,我被我对格式的理解所误导,我现在已经纠正了我的错误。尽管您不需要进行任何更改。我现在正在调查并理解您的回答,感谢您的帮助。
  • 嗨,John,今天早上我设法更好地查看了您的代码并进行了尝试,它没有任何问题。将此与我尝试的进行比较,我现在可以看到我一直在哪里出错!感谢您的回答和帮助。
猜你喜欢
  • 2021-11-29
  • 1970-01-01
  • 2018-06-19
  • 1970-01-01
  • 1970-01-01
  • 2019-04-07
  • 2021-08-09
  • 2017-10-14
  • 2021-03-24
相关资源
最近更新 更多