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

发布于 2024-11-30 14:26:43 字数 1631 浏览 5 评论 0原文

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

混乱细节:

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

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

  • 另外根据这个图,这是否意味着我发送的每个命令都会始终固定 Header?

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

    导入java.io.*;
    导入java.net.*;
    公开课测试 
    {
    
    public static void main(String[] args) 抛出 UnknownHostException、IOException 
    { 
    
        字节st[]=新字节[256];
        st[0]=0x01; // 得到    
        st[1]=0x02; // 标头2字节
        st[2]=0x02; // 标头长度            
        st[3]=0; // 命令字节
        st[4]=0; // 预订的
        st[5]=0;            
        st[6]=0;             
        st[7]=0;
        st[8]=0x01; // 关闭电源
        st[9]=0x30; 
        st[10]=0x01;
        st[11]=0x00; 
        System.out.println(st); // 这应该可行吗,我根据图表正确吗?
    
    
        Socket s = new Socket("192.168.1.2", 49137);
        DataInputStream 输入 = new DataInputStream(s.getInputStream());          
        DataOutputStream outToServer = new DataOutputStream(s.getOutputStream());
        BufferedReader i = new BufferedReader(new InputStreamReader(s.getInputStream()));
        outToServer.write(st);
        字符串获取;
        获取 = i.readLine();
        System.out.println("来自服务器:" + get);
        s.close();        
    }
    

    }

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

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

I am very confused with this kind of stuffs, What should I send as final command, get always confused 8 bits to 1 byte but how do we make it? Is it only the command packet [hex] as shown in screen shot? or Is it header + command packet[hex] shown in two screen shots?

Confusion details:

  • In such diagram header block shows mostly like "Bit 7, Bit 6,..,Bit 0" instead of "Bit 0, Bit 1, Bit 2, ... Bit 7", i always wondering why?.

  • But when I apply in code what is following order for byte st[0] = Bit 7 or Bit 1?

  • Also according to this diagram, does it mean every command I send will have Header fixed always?

  • This is the code I was trying by taking Bit 1 as st[0], Bit 2 as st1 instead of Bit 7 as st[0]. To apply Power off/on test.

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

    }

enter image description here
enter image description here

P.S: How would you do this really? Every hex command make hand by hand, this PDF file has almost 100 commands, it would take lot of time. Or you manage them in a different way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

陌路黄昏 2024-12-07 14:26:43

正如@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。有关单个位上的 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 代码中,您需要使用以下二元运算符:

  • |二进制或
  • 与二进制 AND
  • ~ 二进制 NOT

因此,如果您想在一个字节中设置一个位:

byte anyByte;
anyByte = anyByte | 0x40;

可以缩短为

anyByte |= 0x40;

如果您想清除一个位:

anyByte &= ~0x40;

如果您想测试是否设置了一个位,您可以使用以下命令:

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

如果您想想要测试位 4 和位 1 是否已设置,您需要执行以下操作:

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

为什么是 0x12?因为hex 12二进制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。

As @Rocky mentioned, it looks like in your code you are confusing bits and bytes.

It might help if you think of a byte in binary:

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

If you look at the binary representation, you count the bits from the right: A byte has 8 bits, so bit 7 is the left-most bit and bit 0 is the right-most bit.

The reason why hexadecimal (base-16) notation is so convenient is because it is easier to convert between binary to hex than binary to hex.

Take the binary number 00110000. If you split these up into two parts (0011) and (0000) called the high nibble (bits 7-4) and the low nibble (bits 3-0). Then you can easily convert the two nibbles into hex:

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

Putting the two nibbles together, you can see the relationship between hex and binary:

Binary
0011 1100

Hex
   3    C

so binary 00110100 = hex 34 = dec 60

So back to your binary format:

In the request packet, you are getting the response (hex 30), so if you convert that into your bit:

Hex 30 = binary 0011 0000

You can see that bit 5 and 4 are set.

In order to dynamically set bits in a byte, you'll need to use boolean logic AND and OR. See the following for results of and and or on a single bit:

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

You've also got the NOT operation

Bit            Result NOT
0              1
1              0

With multiple bits, you just perform the operation on each bit (bit 7 to 0), for example:

    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)

So with that in mind, you can use the following operations to set and clear individual bits in a byte:

If you want to ensure that bit 6 is set (1), you just have to OR it with 01000000 (hex 40)

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

If you wanted to ensure that bit 6 is clear (0), you just have to AND it with NOT (hex 40), so

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

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

To put all this into Java code, you've got the following binary operators:

  • | binary OR
  • & binary AND
  • ~ binary NOT

So if you wanted to set a bit in a byte:

byte anyByte;
anyByte = anyByte | 0x40;

which can be shortened to

anyByte |= 0x40;

If you want to clear a bit:

anyByte &= ~0x40;

If you want to test whether a bit is set, you'd use the following:

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

If you want to test whether bit 4 and bit 1 was set, you'd do the following:

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

Why 0x12? Because hex 12 is binary 0001 0010 which "masks" bit 4 and 1.

Back to your question:

In order to send the correct command string, you just need to create the right byte array as specified in the manual, though I hope it is clearer now how to set bits in bytes:

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

Note, I'm not using a DataInputStream here as the way Java encodes integers may be different from how the device encodes it's integers. E.g. in Java and integer is 4 bytes and Big Endian (see also this SO article).

N.B. the << operator is the left shift operator, which shifts the bits along in a byte and the above case is used to combine two bytes into a 16-bit number. Left-shifting by 8 is equivalent to multiplying by 256.

愛上了 2024-12-07 14:26:43
  • <块引用>

    在这样的图中,标题块主要显示“位 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;
  • <块引用>

    另外根据此图,这是否意味着我发送的每个命令都会始终固定标头

  • <块引用>

    这是我尝试的代码,将位 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;
  • In such diagram header block shows mostly like "Bit 7, Bit 6,..,Bit 0"
    instead of "Bit 0, Bit 1, Bit 2, ... Bit 7", i always wondering
    why?.

In typical number writing 0 is the least significant bit 7 the most significant and the byte is written in most significant to least significant 7-0.

byte  11111111
place 76543210
  • But when i apply in code what is following order for byte st[0] = Bit 7 or Bit 1?

In your code this is not setting bits this is setting a byte in the byte array

If you are nervous about setting bits try a class like this:

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

}

use it in you code like:

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;
  • Also according to this diagram, does it mean every command i send will have Header fixed always

Yes

  • This is the code i was trying by taking Bit 1 as st[0], Bit 2 as st1 instead of Bit 7 as st[0]. To apply Power off/on test.

I don't have enough information to actually build the packet but:

// 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;
送舟行 2024-12-07 14:26:43

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

我将制作一个小方法,以十六进制很好地打印命令缓冲区,而不是 System.out.println(st) 。

You don't need to care about bit order because IP packets contain bytes and the lower level components make sure every byte is transferred correctly.

Instead of System.out.println(st) I'd make a small method that prints the command buffer nicely in hexadeciaml.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文