簡體   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