繁体   English   中英

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

[英]How to send such complicated hex, binary protocol data accurately using java byte[]?

我对这种东西很困惑,作为最终命令我应该发送什么,总是把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(); } 

    }

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

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

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

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

Binary      Decimal     Hex
00000000    0           0x00
00000001    1           0x01
00110000    48          0x30

如果查看二进制表示,则计算右边的位:一个字节有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

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

Binary
0011 1100

Hex
   3    C

so binary 00110100 = hex 34 = dec 60

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

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

Hex 30 = binary 0011 0000

您可以看到第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

你也得到了NOT操作

Bit            Result NOT
0              1
1              0

使用多个位,您只需对每个位(位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)

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

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

    xxxxxxxx    (any value)
 OR 01000000    (hex 40)
  = x1xxxxxx

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

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

    xxxxxxxx    (any value)
AND 10111111    (hex BF)
  = x0xxxxxx

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

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

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

byte anyByte;
anyByte = anyByte | 0x40;

这可以缩短为

anyByte |= 0x40;

如果你想清除一点:

anyByte &= ~0x40;

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

if ((anyByte & 0x40) == 0x40) ...

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

if ((anyByte & 0x12) == 0x12) ...

为什么是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();

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

注意, <<运算符是左移位运算符,它将位移位在一个字节中,上面的情况用于将两个byte组合成一个16位数。 左移8等于乘以256。

您不需要关心位顺序,因为IP数据包包含字节,较低级别的组件确保每个字节都正确传输。

而不是System.out.println(st)我做了一个小方法,在hexadeciaml中很好地打印命令缓冲区。

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

在典型的数字写入中,0是最低有效位7是最重要的,而字节是以最重要到最低有效7-0写入。

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

在你的代码中,这不是设置位,而是在字节数组中设置一个字节

如果你对设置位感到紧张,请尝试这样的类:

public class SimpleByteSetter {

  /* b is is byte you are setting
     on is if the bit is set to on or 1
     place is the bit place in the range of 0-7
  */
  public static byte set(final byte b, final boolean on, final int place) {
    if (on) { return (byte) (b | ((1 << place) & 0xFF)); }
    return (byte) (b & (~((1 << place) & 0xFF)));
  }

  // 1 == on everything else off (but only use 0!)
  public static byte set(final byte b, final int on, final int place) {
    return set(b, 1==on, place);
  }

}

在你的代码中使用它:

byte header = 0;
// get = 0, set = 1, place = 0
header = SimpleByteSetter(header, 0, 0 ); // get
// header 2 byte = 0, header 2 byte = 1, place = 1
header = SimpleByteSetter(header, 0, 1 ); // header 1 byte
...
st[0] = header;
  • 同样根据此图,它是否意味着我发送的每个命令都将始终固定Header

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

我没有足够的信息来实际构建数据包,但是:

// the header is 1 byte I don't see how to make it two
// the command is 2 bytes per the table
// the parameter length is 0 so 1 byte (is there suppose to be a device id?)
byte[] st = new byte[ 1 + 2 + 1 ];

byte header = 0;
// get = 0, set = 1, place = 0
header = SimpleByteSetter(header, 0, 0 ); // get
// header 2 byte = 0, header 2 byte = 1, place = 1
header = SimpleByteSetter(header, 0, 1 ); // header 1 byte
// length 1 byte = 0, length 2 byte = 1
header = SimpleByteSetter(header, 0, 2 );
// command 1 byte = 0, command 2 byte = 1; 
header = SimpleByteSetter(header, 1, 3 );

st[0] = header;
st[1] = 0x0130 & 0xFF; // poweroff command first byte
st[2] = 0x0100 & 0xFF; // poweroff second byte
st[3] = 0;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM