如何使用java byte []准确发送这种复杂的十六进制,二进制协议数据?

4 java protocols

我对这种东西很困惑,作为最终命令我应该发送什么,总是把8位纠缠到1个字节,但我们如何制作呢?是否只有屏幕截图中显示的命令包[hex]?或是两个屏幕截图中显示的标题+命令包[hex]?

混乱细节:

  • 在这样的图表中,标题块显示大部分像"位7,位6,..,位0"而不是"位0,位1,位2,...位7",我总是想知道为什么?

  • 但是当我在代码中应用字节st [0] =位7或位1的顺序时?

  • 同样根据这个图表,它是否意味着我发送的每个命令都会始终修复Header?

  • 这是我尝试的代码,将位1作为st [0],位2作为st 1而不是位7作为st [0].应用电源关/开测试.

    import java.io.*;
    import java.net.*;
    public class test 
    {
    
    public static void main(String[] args) throws UnknownHostException, IOException 
    { 
    
        byte st[]=new byte[256];
        st[0]=0x01; // get    
        st[1]=0x02; // header 2 byte
        st[2]=0x02; // header length            
        st[3]=0;    // command byte
        st[4]=0;    // reserved
        st[5]=0;            
        st[6]=0;             
        st[7]=0;
        st[8]=0x01; // power off
        st[9]=0x30; 
        st[10]=0x01;
        st[11]=0x00; 
        System.out.println(st); // Should this work, am i correct based on diagram?
    
    
        Socket s           = new Socket("192.168.1.2", 49137);
        DataInputStream input         = new DataInputStream(s.getInputStream());          
        DataOutputStream outToServer  = new DataOutputStream(s.getOutputStream());
        BufferedReader i   = new BufferedReader(new InputStreamReader(s.getInputStream()));
        outToServer.write(st);
        String get;
        get = i.readLine();
        System.out.println("FROM SERVER: " + get);
        s.close();        
    }
    
    Run Code Online (Sandbox Code Playgroud)

    }

在此输入图像描述 在此输入图像描述

PS:你真的会怎么做?每个十六进制命令都是手工制作,这个PDF文件有近100个命令,需要很多时间.或者你以不同的方式管理它们?

ben*_*y23 7

正如@Rocky所提到的,在您的代码中,您看起来很混淆位和字节.

如果你想到二进制中的一个字节,它可能会有所帮助:

Binary      Decimal     Hex
00000000    0           0x00
00000001    1           0x01
00110000    48          0x30
Run Code Online (Sandbox Code Playgroud)

如果查看二进制表示,则计算右边的位:一个字节有8位,因此第7位是最左边的位,第0位是最右边的位.

十六进制(base-16)表示法如此方便的原因是因为在二进制到十六进制之间转换比二进制到十六进制更容易.

取二进制数00110000.如果将它们分成两部分(0011)和(0000),称为高半字节(位7-4)和低半字节(位3-0).然后你可以轻松地将两个半字节转换为十六进制:

Nibble    Hex     Decimal
0000      0       0
0001      1       1
0010      2       2
0011      3       3
0100      4       4
0101      5       5
0110      6       6
0111      7       7
1000      8       8
1001      9       9
1010      A       10
1011      B       11
1100      C       12
1101      D       13
1110      E       14
1111      F       15
Run Code Online (Sandbox Code Playgroud)

将两个半字节组合在一起,您可以看到十六进制和二进制之间的关系:

Binary
0011 1100

Hex
   3    C

so binary 00110100 = hex 34 = dec 60
Run Code Online (Sandbox Code Playgroud)

所以回到你的二进制格式:

在请求数据包中,您将收到响应(十六进制30),因此如果您将其转换为您的位:

Hex 30 = binary 0011 0000
Run Code Online (Sandbox Code Playgroud)

您可以看到第5位和第4位已设置.

为了动态设置字节中的位,您需要使用布尔逻辑AND和OR.请参阅以下内容以获取和/或在单个位上的结果:

Bit    Bit     Result Result Result
A      B       AND    OR     XOR
0      0       0      0      0
0      1       0      1      1
1      0       0      1      1
1      1       1      1      0
Run Code Online (Sandbox Code Playgroud)

你也得到了NOT操作

Bit            Result NOT
0              1
1              0
Run Code Online (Sandbox Code Playgroud)

使用多个位,您只需对每个位(位7到0)执行操作,例如:

    01101010    (hex 6A)
AND 11100110    (hex E6)
  = 01100010    (hex 62)

    01101010    (hex 6A)
 OR 11100110    (hex E6)
  = 11101110    (hex EE)

NOT 00111001    (hex 3B)
  = 11000110    (hex C6)
Run Code Online (Sandbox Code Playgroud)

因此,考虑到这一点,您可以使用以下操作来设置和清除字节中的各个位:

如果要确保设置位6(1),则只需将其与01000000(十六进制40)进行或运算

    xxxxxxxx    (any value)
 OR 01000000    (hex 40)
  = x1xxxxxx
Run Code Online (Sandbox Code Playgroud)

如果你想确保第6位是清除的(0),你只需要将它与NOT(十六进制40)AND,所以

NOT 01000000    (hex 40)
  = 10111111    (hex BF)

    xxxxxxxx    (any value)
AND 10111111    (hex BF)
  = x0xxxxxx
Run Code Online (Sandbox Code Playgroud)

要将所有这些都放入Java代码中,您需要以下二元运算符:

  • | 二进制OR
  • &二进制AND
  • 〜二进制NOT

所以如果你想在一个字节中设置一个位:

byte anyByte;
anyByte = anyByte | 0x40;
Run Code Online (Sandbox Code Playgroud)

这可以缩短为

anyByte |= 0x40;
Run Code Online (Sandbox Code Playgroud)

如果你想清除一点:

anyByte &= ~0x40;
Run Code Online (Sandbox Code Playgroud)

如果要测试是否设置了某个位,则使用以下命令:

if ((anyByte & 0x40) == 0x40) ...
Run Code Online (Sandbox Code Playgroud)

如果要测试是否设置了第4位和第1位,则执行以下操作:

if ((anyByte & 0x12) == 0x12) ...
Run Code Online (Sandbox Code Playgroud)

为什么是0x12?因为hex 12binary 0001 0010"掩盖"第4位和第1位.

回到你的问题:

为了发送正确的命令字符串,您只需要创建手册中指定的正确字节数组,但我希望现在更清楚如何设置以字节为单位的位:

Socket s = new Socket("192.168.1.2", 49137);
InputStream in = s.getInputStream());          

// send the request 
// (Note, for different requests, you'll need different, byte arrays and may
// even have different lengths (can't tell without having seen the whole manual)
byte[] st = new byte[] { 0x01, 0x30, 0x01, 0x00 };
OutputStream out = s.getOutputStream();
out.write(st);
out.flush();

// read the first header byte (bits indicate the rest of the header structure)
byte header = (byte)in.read();

// bit 1 shows whether the header represented in 2 bytes
boolean header2Byte = (header & 0x2) != 0;
// bit 2 shows whether the length is represented in 2 bytes
boolean len2Byte = (header & 0x4) != 0;

if (header2Byte) {
    // need to read the extra header (discarded here)
    in.read();
}

// missed off reading the command byte/s

int len = 0;
if (len2Byte) {
    byte[] lenByte = new byte[2];
    in.read(lenByte);

    if (isLittleEndian) {
        len = (lenByte[1] << 8) + lenByte[0];
    } else {
        len = (lenByte[0] << 8) + lenByte[1];
    }
} else {
    // only one byte signifies the length
    len = is.read();
}

byte[] data = new byte[len];
in.read(data);

// it is unclear what format the data, has but if it is a string, and encoded as
// UTF-8, then you can use the following
String stringData = new String(data, "UTF-8");

System.out.println(stringData);

// note, try-catch-finally omitted for brevity

in.close();
out.close();
s.close();
Run Code Online (Sandbox Code Playgroud)

注意,我这里没有使用DataInputStream,因为Java编码整数的方式可能与设备编码整数的方式不同.例如,Java和整数是4字节和Big Endian(另见这篇SO文章).

NB <<运算符是左移位运算符,它将位移位一个字节,上面的情况用于将两个bytes 组合成一个16位数.左移8等于乘以256.