There is a built-in byte type in Java, and you can only read byte[] buffers using InputStream # read (byte []) and write to OutputStream using OutputStream # write (byte [], int, int) , so there is no problem .
As for your messages - as you noted correctly, the minimum bit of information that you receive at one time is a byte, so you will first have to decompose your message format into 8 bits:
Suppose your message is in bytes [] named data. I also assume a little.
A uint32 has a length of 32 bits → four bytes. (Be careful when analyzing this in Java, Java integers and longs are signed, you need to handle it. An easy way to avoid trouble is to take a lot of time. Data [0] fills bits 31 - 24, data [1] 23 - 16, data [2] bits 15 to 8 and data [3] bits 7 to 0. Therefore, you need to accordingly move them to the left and glue them together with a logical OR:
long uint32 = ((data[0]&0xFF) << 24) | ((data[1]&0xFF) << 16) | ((data[2]&0xFF) << 8) | (data[3]&0xFF);
Further, there are two separate bits. I suppose you need to check if they are "on" (1) or "off" (0). To do this, you use bit masks and compare your byte with logical I.
First bit: (binary mask | 1 0 0 0 0 0 0 0 0 | = 128 = 0x80)
if ( (data[4] & 0x80 ) == 0x80 ) // on
The second bit: (binary mask | 0 1 0 0 0 0 0 0 | = 64 = 0x40)
if ( (data[4] & 0x40 ) == 0x40 ) // on
To compose the next uint32, you have to compose bytes above the byte boundaries of the underlying data. For example. for the first byte, take the remaining 6 bits of data [4], move them two to the left (they will be bits 8 to 2 of uint32) and "add" the first (highest) two data [5], moving them 6 bits to the right (they will take the remaining 1 and 0 uint32 slots). "Add" means logically OR'ing:
byte uint32Byte1 = (byte)( (data[4]&0xFF) << 2 | (data[5]&&0xFF) >> 6);
Building your uint32 will be the same as in the first example. And so on and so forth.