0

我有一个二进制协议,它将每个有效载荷字节的 MSB 提取到一个 MSB 收集字节(septett)中进行传输,并在接收器端重新注入 MSB。有效载荷由 n 个四字节帧组成,具体取决于发送者(在我的情况下为 6 个)。

这是两个示例帧,它们的 septett(最后一个字节),如在线上所示:

0x2E 0x00 0x5F 0x00 0x04
0x79 0x01 0x38 0x22 0x04

这些是相同的帧,客户端,重新注入了 MSB:

0x2E 0x00 0xDF 0x00
0x79 0x01 0xB8 0x22

进行转换的 C 函数在本文档的第 9 页和第 10 页中定义。我的 Java 版本如下。我遇到的问题是这些都不起作用,我很困惑为什么。我从电线中传递了我的四个字节,并得到相同的字节,原封不动。我可以使用一些帮助来找出这里出了什么问题(可能是一些我看不到的微不足道的事情)。

private static byte[] vbusExtractSeptett(byte[] data, int offset, int length) {
    byte septett = 0;
    for (int i = 0; i < length; i++) {
        if ((data[offset + i] & 0x80) != 0) {
            data[offset + i] &= 0x7F;
            septett |= 1 << i;
        }
    }

    data[offset + length] = septett;
    return data;
}

private static byte[] vbusInjectSeptett(final byte[] data, int offset, int length) {
    byte septett = data[offset + length];

    for (int i = 0; i < length; i++) {
        if ((septett & (1 << i)) != 0)
            data[offset + i] |= 0x80;
    }

    return data;
}
4

2 回答 2

1

在 Java 中,一个字节是有符号的。如果不阅读您的所有代码,我敢打赌这就是问题所在。

文档中的 C 代码使用 unsigned char 数学。

由于 Java 没有“无符号”,您可能需要在短裤(或整数)中进行所有数学运算,然后再转换回字节。一定要屏蔽掉符号位,例如

byte theResult = theIntIDidtheMathOn & 0xFF;
data[index] = theResult;
于 2012-04-24T03:30:58.490 回答
0

另一个答案是正确的,您没有考虑到 Java 对有符号字节的使用。

解决这个问题有几种可能性:

  1. 你可以用“& 0xFF”做上面的事情
  2. 您可以将所有内容都视为整数(如下所示)
  3. 您可以使用图书馆来提供帮助。一些例子是JOOU(它是为了响应这种缺乏无符号类型而开始的)或netty通道缓冲区(注意:虽然 netty 专注于网络 IO,如套接字等,但它的通道缓冲区类非常适合处理字节流和有符号/无符号不同长度的类型,我已经用它来转换二进制协议,就像你正在处理的那样。)

我在下面为您的问题写了一个“解决方案”。

public class SOAnswer
{
    private static void vbusExtractSeptett(int[] data, int offset, int length) {
        int septett = 0;

        for (int i = 0; i < length; i++) {
            if ((data[offset + i] & 0x80) != 0) {
                data[offset + i] &= 0x7F;
                septett |= 1 << i;
            }
        }

        data[offset + length] = septett;
    }

    private static void vbusInjectSeptett(final int[] data, int offset, int length) {
        int septett = data[offset + length];

        for (int i = 0; i < length; i++) {
            if ((septett & (1 << i)) != 0)
                data[offset + i] |= 0x80;
        }

        // clear the septett byte
        data[offset + length] = 0x00;
    }

    private static void printIntArrayAsHEX(int[] array)
    {
        StringBuilder builder = new StringBuilder();

        for ( int a : array )
        {
            String s = Integer.toHexString( a );
            if (s.length() == 1)
                builder.append( "0" );
            builder.append(s + ":");
        }

        builder.substring( 0, builder.lastIndexOf( ":" ) - 1 );

        System.out.println(builder.toString());
    }

    public static void main( String[] args )
    {
        // Create an array long enough for the extracting/injecting
        int[] arr = new int[]{0x2E, 0x00, 0xDF, 0x00, 0x00};
        // see what it looks like
        printIntArrayAsHEX(arr);

        // perform extraction
        vbusExtractSeptett( arr, 0, 4 );
        // see what it looks like
        printIntArrayAsHEX(arr);

        // Perform injection
        vbusInjectSeptett( arr, 0, 4 );
        // see what it looks like
        printIntArrayAsHEX(arr);
    }
}

我对您的一个建议是考虑您是否真的想逐字重新实现 C 代码(尤其是传递原始类型数组、数组中的偏移量和数组长度的函数式编程风格)。也许更像这样的东西会更面向对象:

private static int[] vbusExtractSeptettJAVASTYLE(int[] data) {
    int[] extractedData = Arrays.copyOf( data, data.length +1 );

    int septett = 0;

    for (int i = 0; i < data.length; i++) {
        if ((data[i] & 0x80) != 0) {
            extractedData[i] = data[i] &= 0x7F;
            septett |= 1 << i;
        }
    }

    extractedData[extractedData.length-1] = septett;

    return extractedData;
}
于 2012-04-24T04:32:51.547 回答