I have a socket server which keeps listening to incoming requests. The data received will be in the form of binary array of bytes.
Data format is something like this.
2321902321221200AA
Whereas 1 byte is data begin
4 bits is version
4 bits is data return type
5 bytes are product code
2 bytes data length
The question is, how to parse the data and segregate the parameters.
Thanks in advance!!
Try java.io.DataInputStream:
DataInputStream dis = new DataInputStream(in);
byte b = dis.readByte();
int version = (b >> 4) & 0xF;
int returnType = b & 0xF;
byte[] productCode = new byte[5];
dis.readFully(productCode);
int len = dis.readShort() & 0xFFFF;
if use the java binary block parser then code will look like
class Parsed {
#Bin byte begin;
#Bin(type = BinType.BIT) int version;
#Bin(type = BinType.BIT) int returnType;
#Bin byte [] productCode;
#Bin(type = BinType.USHORT) int dataLength;
}
final Parsed parsed = JBBPParser.prepare("byte begin; bit:4 version; bit:4 returnType; byte [5] productCode; ushort dataLength;")
.parse(new byte[]{0x23,0x21,(byte)0x90,0x23,0x21,0x22,0x12,0x00,(byte)0xAA})
.mapTo(Parsed.class);
assertEquals(0x23, parsed.begin);
assertEquals(0x01, parsed.version);
assertEquals(0x02, parsed.returnType);
assertArrayEquals(new byte[]{(byte)0x90,0x23,0x21,0x22,0x12}, parsed.productCode);
assertEquals(0x00AA,parsed.dataLength);
try {
char [] cbuf = new char[16];
char databegin = cbuf[0];
char [] version = Arrays.copyOfRange(cbuf, 1, 6)
char [] product_typep = Arrays.copyOfRange(cbuf, 7, 12)
char []data_lendth = Arrays.copyOfRange(cbuf, 13, 15)
} catch(Error e){
System.out.println(e);
}
byte [] data = receiveData ();
int dataBegin = data [0]; // Once field is 1-byte, it is simple!
int version = data [1] & 0x0F; // Use shift (>>>) and binary "and" (&)
int returnCode = // to extract value of fields that are
(data [1] >>> 4) & 0x0F; // smaller than one byte
byte [] productCode = // Copy fixed-size portions of data
new byte [] { // into separate arrays using hardcode
data [2], data [3], // (as here), or System.arrayCopy
data [4], data [5], // in case field occupies quite
data [6]}; // a many bytes.
int dataLength = // Use shift (<<) binary or (|) to
(data [7] & 0xFF) | // Combine several bytes into one integer
((data [8] & 0xFF) << 8); // We assume little-endian encoding here
I would got for some king of package reader:
class Record {
.....
Record static fromBytes(byte[] bytes) {
// here use ByteBuffer or DataInputStream to extract filds
......
}
}
Record readNextRecord(InputStream in) {
int len = in.read() && 0xFF;
byte[] data = new byte[len];
in.read(data);
return Record.fromBytes(data)
}
{
InputStream in = ....;
Record r readNextRecord(in);
process (r);
}
Of course you need to add error handling. In general, for something which should run reliable, I will suggest to use NIO framework like Grizzly or Netty.
You might get the data via the ByteArrayOutputStream
And then parse the bytes by applying masks (mainly AND, OR).
Take a look at This question
Hope this helps
Related
I am trying the following:
C# Client:
string stringToSend = "Hello man";
BinaryWriter writer = new BinaryWriter(mClientSocket.GetStream(),Encoding.UTF8);
//write number of bytes:
byte[] headerBytes = BitConverter.GetBytes(stringToSend.Length);
mClientSocket.GetStream().Write(headerBytes, 0, headerBytes.Length);
//write text:
byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(stringToSend);
writer.Write(textBytes, 0, textBytes.Length);
Java Server:
Charset utf8 = Charset.forName("UTF-8");
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), utf8));
while (true) {
//we read header first
int headerSize = in.read();
int bytesRead = 0;
char[] input = new char[headerSize];
while (bytesRead < headerSize)
{
bytesRead += in.read(input, bytesRead, headerSize - bytesRead);
}
String resString = new String(input);
System.out.println(resString);
if (resString.equals("!$$$")) {
break;
}
}
The string size equals 9.That's correct on both sides.But, when I am reading the string iteself on the Java side, the data looks wrong.The char buffer ('input' variable)content looks like this:
",",",'H','e','l','l','o',''
I tried to change endianness with reversing the byte array.Also tried changing string encoding format between ASCII and UTF-8.I still feel like it relates to the endianness problem,but can not figure out how to solve it.I know I can use other types of writers in order to write text data to the steam,but I am trying using raw byte arrays for the sake of learning.
These
byte[] headerBytes = BitConverter.GetBytes(stringToSend.Length);
are 4 bytes. And they aren't character data so it makes no sense to read them with a BufferedReader. Just read the bytes directly.
byte[] headerBytes = new byte[4];
// shortcut, make sure 4 bytes were actually read
in.read(headerBytes);
Now extract your text's length and allocate enough space for it
int length = ByteBuffer.wrap(headerBytes).getInt();
byte[] textBytes = new byte[length];
Then read the text
int remaining = length;
int offset = 0;
while (remaining > 0) {
int count = in.read(textBytes, offset, remaining);
if (-1 == count) {
// deal with it
break;
}
remaining -= count;
offset += count;
}
Now decode it as UTF-8
String text = new String(textBytes, StandardCharsets.UTF_8);
and you are done.
Endianness will have to match for those first 4 bytes. One way of ensuring that is to use "network order" (big-endian). So:
C# Client
byte[] headerBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(stringToSend.Length));
Java Server
int length = ByteBuffer.wrap(headerBytes).order(ByteOrder.BIG_ENDIAN).getInt();
At first glance it appears you have a problem with your indexes.
You C# code is sending an integer converted to 4 bytes.
But you Java Code is only reading a single byte as the length of the string.
The next 3 bytes sent from C# are going to the three zero bytes from your string length.
You Java code is reading those 3 zero bytes and converting them to empty characters which represent the first 3 empty characters of your input[] array.
C# Client:
string stringToSend = "Hello man";
BinaryWriter writer = new BinaryWriter(mClientSocket.GetStream(),Encoding.UTF8);
//write number of bytes: Original line was sending the entire string here. Optionally if you string is longer than 255 characters, you'll need to send another data type, perhaps an integer converted to 4 bytes.
byte[] textBytes = System.Text.Encoding.UTF8.GetBytes(stringToSend);
mClientSocket.GetStream().Write((byte)textBytes.Length);
//write text the entire buffer
writer.Write(textBytes, 0, textBytes.Length);
Java Server:
Charset utf8 = Charset.forName("UTF-8");
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream(), utf8));
while (true) {
//we read header first
// original code was sending an integer as 4 bytes but was only reading a single char here.
int headerSize = in.read();// read a single byte from the input
int bytesRead = 0;
char[] input = new char[headerSize];
// no need foe a while statement here:
bytesRead = in.read(input, 0, headerSize);
// if you are going to use a while statement, then in each loop
// you should be processing the input but because it will get overwritten on the next read.
String resString = new String(input, utf8);
System.out.println(resString);
if (resString.equals("!$$$")) {
break;
}
}
So I have some FRX binary files from which I am attempting to get string captions using Java's binary reading methods.
I was capable of doing so, and specifying the region in which to read bytes in C# using the following program :
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
public class GetFromFRX
{
public static void Main()
{
StringBuilder buffer = new StringBuilder();
using (BinaryReader b = new BinaryReader(File.Open("frmResidency.frx", FileMode.Open)))
{
try
{
b.BaseStream.Seek(641, SeekOrigin.Begin);
int length = b.ReadInt32();
for (int i = 0; i < length; i++)
{
buffer.Append(b.ReadChar());
}
}
catch (Exception e)
{
Console.WriteLine( "Error obtaining resource\n" + e.Message);
}
}
Console.WriteLine(buffer);
}
}
Question Update :
Attempting to do the same in Java, I have built the following program. Now I have implemented Guava in order to use LittleEndian equivalents, however now my length prints 24, and as a result I only get the first 24 bytes in my output file. Is ReadInt not appropriate for this situation, and function in a different manner than ReadInt32?
import java.io.*;
import com.google.common.io.*;
public class RealJavaByteReader {
public static void main(String[] args) throws IOException {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("frmResidency.frx");
LittleEndianDataInputStream din = new LittleEndianDataInputStream(in);
out = new FileOutputStream("output.txt");
int length = din.readInt();
System.out.println(length);
int c;
for (c = 0; c < length; c++) {
// TODO: first read byte and check for EOF
out.write(din.read());
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
}
}
Elizion,
This might be because you might be reading an int stored using little endian. As such, Java uses Big endian and .NET little endian.
Use a function as below to convert a little endian int to a big endian int in java.
/**
* Byte swap a single int value.
*
* #param value Value to byte swap.
* #return Byte swapped representation.
*/
public static int swap (int value)
{
int b1 = (value >> 0) & 0xff;
int b2 = (value >> 8) & 0xff;
int b3 = (value >> 16) & 0xff;
int b4 = (value >> 24) & 0xff;
return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0;
}
Please try to see the below post.
Converting Little Endian to Big Endian
I realized what my mistake was at this point. Now that LittleEndianDataInputStream has been implemented, I can correctly use SkipBytes to set my initial byte position, and will return the string caption as required. Of course I will initially only produce the first 24 bytes, as whatever is in the first 4 bytes of the binary file must hold a length of 24 for some given property in the FRX file. I must set the offset with skipBytes in order to produce anything meaningful, because the lengths of properties in FRX files are stored in groups of 4 bytes and are followed by those bytes containing that property .
For instance, if I set din.skipBytes(308);, then the 308th to 312th bytes in the FRX file hold the byte-length of the string in the Caption property I require (for instance 140), which is output by readInt. Thus the next 140 bytes will contain my string required, and my for loop will iterate properly.
i am developing an Android application and I am having some issues with adding bytes to compose a data packet. I have created a method to get the MAC Address of the client and the return value is a string (MACAdd).
private String getMACAddress() {
WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo info = manager.getConnectionInfo();
MACAdd= info.getMacAddress();
return MACAdd;
}
I have declared MACAddrs as a static String and have done the following assignment in createPackect() method.
MACAddrs = getMACAddress();
byte [] MACAddres = MACAddrs.getBytes();
In the same createPacket() method, I have declared following data as well.
byte Node = (byte)0x3039;
byte Cost = (byte)0x1;
byte MaxMsgSize = (byte) 0x100000;
byte KeepAlivePiriod = (byte)0x2710;
byte NetworkID = (byte)0x1;
I need to add these bytes into a single array to compose the data packet.
byte[] BroadcastPacket = new byte[Node + Cost + SerialNumber + MaxMsgSize + KeepAlivePiriod + NetworkID + MACAddres + IPAddress];
as I am adding two different byte types, I am having a error. Please advise me how to solve this issue......
A convenience method to concatenate byte arrays might help:
byte[] concatArrays(byte[] ... arrays) {
int totalLength = 0;
for (byte[] arr : arrays) {
totalLength += arr.length;
}
byte[] result = new byte[totalLength];
int start = 0;
for (byte[] arr : arrays) {
System.arraycopy(arr, 0, result, start, arr.length);
start += arr.length;
}
return result;
}
Using this, you could create byte[] BroadcastPacket like this:
byte[] BroadcastPacket = concatArrays(
new byte[Node + Cost + SerialNumber + MaxMsgSize + KeepAlivePiriod + NetworkID],
MACAddrs.getBytes(),
IPAddrs.getBytes()
);
Btw, you should use camelCase for variable names.
And you don't need to cast such values to byte:
byte cost = (byte) 0x1;
This works just as well:
byte cost = 0x1;
If the value you're trying to assign to a byte doesn't actually fit into a byte,
casting to byte you are losing precision, for example here:
byte b3 = (byte) 0x100000;
If you don't mind the precision loss by design, that's fine, otherwise this can be a nasty bug.
I want send any value via Bluetooth, by any i mean value 0-255, but I can't convert this value into a char in string. I tried few different ways, but without success.
int a= 240 ;
char z=(char)a;
mConnectedThread.write("START"+"\240"+","+"\0240"+","+"\030"+","+Integer.toString(a)+","+z+","+"\0f1"+"STOP");
I get this (left - value in decimal, right value in ASCII char):
83-'S'
84-'T'
65- 'A'
82- 'R'
84- 'T'
194-'Â'
160- ''
44- ','
20-'\024'
48-'0'
44-','
24-'\030'
44-','
50-'2'
52-'4'
48-'0'
44-','
195-'Ã'
176-'°'
44 ','
0-'\0'
102-'f'
49-'1'
83-'S'
84-'T'
79-'O'
80-'P'
When I send \030 then I receive 24-'\30' in one character, but I can't send bigger numbers.
So my question is: how to set/convert any value in the range 0-255 in one string character. I don't need to display this, it's not important.
I receive and send byte[] data, and it working good, but i think that makin this on string will be better, bocouse when i make data table it's looks like this
byte[] dat = new byte[50];
dat[i++] = (byte) '!';
dat[i++] = (byte) '!';
dat[i++] = (byte) 0xf0;
dat[i++] = (byte) 0xf0;
dat[i++] = (byte) SET_COUPLING;
dat[i++] = (byte) (coupling + 64*this.name);
dat[i++] = (byte) 0xf0;
dat[i++] = (byte) 0xf0;
dat[i++] = (byte)SET_MULTI;
i just looking for better way to construct this command.
My send function :
void write(String str)
{
if (STATE != CONNECTED)
return;
try
{
mmOutStream.write(str.getBytes());
} catch (IOException e)
{
synchronized (MainActivity.this)
{
btDisconnect();
changeState(CONNECTION_ERROR);
mConnectedThread = null;
}
}
}
or second write function using byte[]:
void write(byte[] data)
{
if (STATE != CONNECTED)
return;
try
{
mmOutStream.write(data);
} catch (IOException e)
{
handler.post(new Runnable()
{
public void run()
{
btDisconnect();
changeState(CONNECTION_ERROR);
mConnectedThread = null;
}
});
}
}
i see that i convert this string into byte[] using mmOutStream.write(str.getBytes());, but i was thinking that making string command and then convert int byte[] is more elegant method.
I thought that is simple way to make this like in sprintf
sprintf(str,"%4.1f\xdf""C", temp);
where "\xdf" is just 223 in one byte and is degree mark when i put this into alphanumeric display
Edited
If you just want to write a string to the stream without converting it to bytes, you can use BufferedWriter :
OutputStreamWriter osw = new OutputStreamWriter(outputStream);
BufferedWriter bufferedWriter = new BufferedWriter(osw);
bufferedWriter.write("some string");
Make sure that you understand flush and close methods from the api:
http://docs.oracle.com/javase/7/docs/api/java/io/BufferedWriter.html
This is very standard in java to wrap the low level input/output stream objects with higher level Reader/Writer object to get access to more convenient methods.
Original answer
You can construct String using specific charset to decode bytes :
http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#String(byte[], java.nio.charset.Charset)
US-ASCII could be enough for your use case
http://docs.oracle.com/javase/7/docs/api/java/nio/charset/StandardCharsets.html
So you can construct a string out of byte array like this :
new String(byteArray, StandardCharsets.US_ASCII);
Is there any way to convert Java String to a byte[] (not the boxed Byte[])?
In trying this:
System.out.println(response.split("\r\n\r\n")[1]);
System.out.println("******");
System.out.println(response.split("\r\n\r\n")[1].getBytes().toString());
and I'm getting separate outputs. Unable to display 1st output as it is a gzip string.
<A Gzip String>
******
[B#38ee9f13
The second is an address. Is there anything I'm doing wrong? I need the result in a byte[] to feed it to gzip decompressor, which is as follows.
String decompressGZIP(byte[] gzip) throws IOException {
java.util.zip.Inflater inf = new java.util.zip.Inflater();
java.io.ByteArrayInputStream bytein = new java.io.ByteArrayInputStream(gzip);
java.util.zip.GZIPInputStream gzin = new java.util.zip.GZIPInputStream(bytein);
java.io.ByteArrayOutputStream byteout = new java.io.ByteArrayOutputStream();
int res = 0;
byte buf[] = new byte[1024];
while (res >= 0) {
res = gzin.read(buf, 0, buf.length);
if (res > 0) {
byteout.write(buf, 0, res);
}
}
byte uncompressed[] = byteout.toByteArray();
return (uncompressed.toString());
}
The object your method decompressGZIP() needs is a byte[].
So the basic, technical answer to the question you have asked is:
byte[] b = string.getBytes();
byte[] b = string.getBytes(Charset.forName("UTF-8"));
byte[] b = string.getBytes(StandardCharsets.UTF_8); // Java 7+ only
However the problem you appear to be wrestling with is that this doesn't display very well. Calling toString() will just give you the default Object.toString() which is the class name + memory address. In your result [B#38ee9f13, the [B means byte[] and 38ee9f13 is the memory address, separated by an #.
For display purposes you can use:
Arrays.toString(bytes);
But this will just display as a sequence of comma-separated integers, which may or may not be what you want.
To get a readable String back from a byte[], use:
String string = new String(byte[] bytes, Charset charset);
The reason the Charset version is favoured, is that all String objects in Java are stored internally as UTF-16. When converting to a byte[] you will get a different breakdown of bytes for the given glyphs of that String, depending upon the chosen charset.
String example = "Convert Java String";
byte[] bytes = example.getBytes();
Simply:
String abc="abcdefghight";
byte[] b = abc.getBytes();
Try using String.getBytes(). It returns a byte[] representing string data.
Example:
String data = "sample data";
byte[] byteData = data.getBytes();
You can use String.getBytes() which returns the byte[] array.
You might wanna try return new String(byteout.toByteArray(Charset.forName("UTF-8")))
I know I'm a little late tothe party but thisworks pretty neat (our professor gave it to us)
public static byte[] asBytes (String s) {
String tmp;
byte[] b = new byte[s.length() / 2];
int i;
for (i = 0; i < s.length() / 2; i++) {
tmp = s.substring(i * 2, i * 2 + 2);
b[i] = (byte)(Integer.parseInt(tmp, 16) & 0xff);
}
return b; //return bytes
}
i had to conwert a int to decimal 3 byte 129 to 1 2 9
Byte data
int i1 = 129
int i3 = (i1 / 100);
i1 = i1 - i3*100;
int i2 = (i1 / 10);
i1 = i1 - i2*10;
data [1]= (byte) i1
data [2]= (byte) i2
data [3]= (byte) i3
It is not necessary to change java as a String parameter. You have to change the c code to receive a String without a pointer and in its code:
Bool DmgrGetVersion (String szVersion);
Char NewszVersion [200];
Strcpy (NewszVersion, szVersion.t_str ());
.t_str () applies to builder c ++ 2010