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

前端 未结 3 803
猫巷女王i
猫巷女王i 2021-01-14 02:23

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 [

相关标签:
3条回答
  • 2021-01-14 02:57
    • 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;
    
    0 讨论(0)
  • 2021-01-14 03:01

    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.

    0 讨论(0)
  • 2021-01-14 03:04

    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.

    0 讨论(0)
提交回复
热议问题