Server
When you call the method print
of a PrintStream
passing an array of bytes, it ends up calling this version of the method, which according to the documentation writes the result of String.valueOf(objeto)
. And what happens when we do this with an array?
byte[] HEXA = { (byte) 0x00, (byte) 0x96, (byte) 0x07, (byte) 0xBF };
System.out.println(String.valueOf(HEXA)); // [B@15db9742
It prints something like "[B@15db9742" (the numbers and letters may vary because it is hashcode of the array in question, as explained here). So you’re not actually sending the bytes from the array HEXA
, and yes the characters that correspond to the return of String.valueOf
(what will be this "code" there).
In fact the very System.out
is a PrintStream
, then we can test this behavior by doing System.out.print(HEXA)
, that will produce the same result. This is all to say that using PrintStream::print
maybe it’s not what you need.
For your example, you are not working with text/characters, but with "raw" bytes, so to send these bytes you could use a java.io.DataOutputStream
and your method write
, passing the byte array directly (recommend reading here and here for more details).
And if you’re using Java >= 7, you can also use Try-with-Resources, that already closes the resources automatically, without the need for a block finally
:
// Server
byte[] HEXA = { (byte) 0x00, (byte) 0x96, (byte) 0x07, (byte) 0xBF };
try (ServerSocket serverSocket = new ServerSocket(9090, 0, InetAddress.getByName(null))) {
System.out.println("SOCKET ABERTO");
Socket client = serverSocket.accept();
// enviando os bytes para o client
try (OutputStream out = new DataOutputStream(client.getOutputStream())) {
out.write(HEXA, 0, HEXA.length);
} catch (etc...) {
// mensagens de erro
} // não precisa de finally, recursos são fechados automaticamente
} catch (etc...) {
// mensagens de erro
} // não precisa de finally, recursos são fechados automaticamente
Of course, if you want, you can also use a BufferedOutputStream
, that depending on the case can bring benefits:
try (OutputStream out = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()))) {
out.write(HEXA, 0, HEXA.length);
} catch (IOException e) {
etc...
}
Client
Now to read these bytes, it is worth explaining some details before.
A byte
in Java is a numerical type, which uses 8 bits and is Signed (with signal), that is, accepts positive and negative values - to be more precise, values between -128
and 127
.
Internally, what it has is just bits, and the value is not hexadecimal, or decimal, nothing like that. A byte is a byte (an 8-bit set). Dot.
When we print a byte, it is shown in some way (either base 10, 16, 8, 2, or whatever). But that’s just a representation of the value, not the value itself.
Think of a number - for example, 2
- as a concept: the idea of a certain amount ("two things"). I can represent this idea - this value - in different ways: as the digit 2
, as 2.0
(or 2,0
), as the word "two", "two", and many other formats. Each of these forms is different, but all represent the same value.
Similarly, when you use in your code a literal like 0xBF
, this is just a notation of the language: a form that she set for you to put a hexadecimal number in your code (if you just put BF
, could be confused with a variable/class/method name, for example, then the 0x
at the front indicates that BF
should be interpreted as a number in base 16). But this does not mean that the value of the variable will be in hexadecimal, because internally everything will be converted to bits.
So much so that if you do:
// colocando os valores em hexadecimal
byte[] hex = { (byte) 0x00, (byte) 0x96, (byte) 0x07, (byte) 0xBF };
System.out.println("HEXA:");
for (int i = 0; i < hex.length; i++) {
byte b = hex[i];
System.out.printf("hexa: %02X, oct: %03o dec: %04d, dec 'unsigned':%04d, bin: %s\n",
b, b, b, b & 0xff, Integer.toBinaryString(b & 0xff));
}
// colocando os valores em decimal
byte[] dec = { (byte) 0, (byte) 150, (byte) 7, (byte) 191 };
System.out.println("DEC:");
for (int i = 0; i < dec.length; i++) {
byte b = dec[i];
System.out.printf("hexa: %02X, oct: %03o dec: %04d, dec 'unsigned':%04d, bin: %s\n",
b, b, b, b & 0xff, Integer.toBinaryString(b & 0xff));
}
The exit will be:
HEXA:
hexa: 00, oct: 000 dec: 0000, dec 'unsigned':0000, bin: 0
hexa: 96, oct: 226 dec: -106, dec 'unsigned':0150, bin: 10010110
hexa: 07, oct: 007 dec: 0007, dec 'unsigned':0007, bin: 111
hexa: BF, oct: 277 dec: -065, dec 'unsigned':0191, bin: 10111111
DEC:
hexa: 00, oct: 000 dec: 0000, dec 'unsigned':0000, bin: 0
hexa: 96, oct: 226 dec: -106, dec 'unsigned':0150, bin: 10010110
hexa: 07, oct: 007 dec: 0007, dec 'unsigned':0007, bin: 111
hexa: BF, oct: 277 dec: -065, dec 'unsigned':0191, bin: 10111111
The values that are in both arrays hex
and dec
are exactly the same. The difference is that in the first case I used hexadecimal notation, and in the second I used decimal notation. But the resulting bytes are exactly the same (are the same values, I just used different representations of those same values to create the arrays).
Also notice how I show the same values (the same bytes) in different ways:
- at base 16
- at base 8
- base 10 (and values greater than 127 turn negative numbers because
byte
is a guy Signed - to better understand, read here)
- on base 10, but using a "trick" described here and here to convert it to a
int
and thus have the positive value corresponding to its bits
- in base 2, using the same "trick" of the case above, to see the bits of the number
As I said, all these forms are different representations of the same bits (of the same values). The final result (the "text" or "the digits/characters" we see on the screen) may be different for each case (96
, or 226
, or -106
, etc), but all represent the same value (the same byte
, the same 8-bit sequence).
So you don’t need "convert each hexa byte to decimal". You just need to read the bytes of server. That’s all.
Then, if you want to show them on the screen in decimal (or any other format), just print them using the code above. Something like this:
// Client
byte[] dados = new byte[1024]; // cria um buffer para ler vários dados de uma vez
try (Socket socket = new Socket(InetAddress.getByName(null), 9090)) {
InputStream in = new DataInputStream(socket.getInputStream());
int lidos;
while ((lidos = in.read(dados)) > 0) { // enquanto tiver dados para ler
System.out.println("dados lidos do server:");
for (int i = 0; i < lidos; i++) {
byte b = dados[i];
System.out.printf("byte lido - hexa: %02X, dec: %03d\n", b, b & 0xff);
}
}
} catch (IOException e) {
// tratar erros, etc
} // não precisa de finally, recursos são fechados automaticamente
The exit will be:
dados lidos do server:
byte lido - hexa: 00, dec: 000
byte lido - hexa: 96, dec: 150
byte lido - hexa: 07, dec: 007
byte lido - hexa: BF, dec: 191
Or, if you want the values as a int
:
while ((lidos = in.read(dados)) > 0) {
for (int i = 0; i < lidos; i++) {
int x = dados[i] & 0xff;
System.out.println(x);
}
}
That will print:
0
150
7
191
But if you’re using Java >= 8, you can also use Byte.toUnsignedInt
:
while ((lidos = in.read(dados)) > 0) {
System.out.println("dados lidos do server:");
for (int i = 0; i < lidos; i++) {
System.out.println(Byte.toUnsignedInt(dados[i]));
}
}
Which produces the same result above.