The problem:
I need to implement a network input reader from an electronic device which has fixed definitions of a packet format. It has a header, footer and data body. The fields in the packet are defined in term of bits.
Example:
The field sequence_number variable is the bits numbered 8 to 31 and ack_status is bit 32. How do I create a custom serialization function so that I can convert a java object to and from a byte[] read from a packet?
In a 32 Bit Packet
StartOfPacketHeader 4bits
CommandCode 4bits
SequenceNumber 23bits
AckStatus 1bit
How do I make a Java wrapper class for this object with a byte[] constructor and a toBytes() method.
Using the Serializable Interface has no control on individual data sizes. The data sizes may be odd values like a 3-bit or 18-bit variable. I cannot specify the length of the variables in terms of bits.
What I have done so far:
The source actually sends unsigned integers, meaning Java int type cannot handle its entire range. I need to define all data types as long to handle all cases. I have used the BitSet class to carry out byte[] to Long conversion but it's just too tedious and confusing as bitset reverses the bit order to Little Endian by default.
Summary:
How do I convert to my object from the bytes[] I read from the network and vice versa with custom definitions for each field in the most efficient way?
You use DataInputStream and DataOutputStream for this, with a BufferedInputStream or BufferedOutputStream under them respectively for efficiency purposes. The bit fields you will have to program yourself, but these classes provide all the Java primitives in network byte order.
For the packet you posted, you need something like this:
class Packet
{
private byte header;
private byte commandCode;
private int sequenceNumber;
private boolean ackStatus;
void write(DataOutput out) throws IOException
{
int wirePacket = header|(commandCode << 4)|(sequenceNumber << 8);
if (ackStatus)
{
wirePacket |= 0x80000000;
}
out.writeInt(wirePacket);
}
static Packet read(DataInput in) throws IOException
{
Packet packet = new Packet();
int wirePacket = in.readInt();
packet.header = (byte)(wirePacket & 0x0f);
packet.commandCode = (byte)((wirePacket >>> 4) & 0x0f);
packet.sequenceNumber = (wirePacket >>> 8) & 0x7FFFFF;
packet.ackStatus = (wirePacket & 0x80000000) != 0;
return packet;
}
// getters and setters. The setters must ensure that the values don't go out of range.
}
Related
I'm trying to create a custom inputstream. My problem is, the read() method returns an integer from 0-255, but I need to convert it to a byte, decrypt it, and convert it back to an integer. How?
I need something like:
InputStream in = ...;
OutputStream out = ...;
int unsigned = in.read();
byte signed = unsignedIntToSignedByte(unsigned); // from -128 to 127
... // Editing it here
outputstream.write(signedByteToUnsignedInt(signed)); // from 0 - 255
Noting that creating your own encryption is unsafe, and assuming you're doing it "just for fun" and don't think in any way that what you're doing is secure, you really don't need anything special...
int i = in.read();
byte b = (byte) i;
byte e = encrypt(b);
out.write(e);
would be the basic approach, assuming byte encrypt(byte b) method which does the "encryption". Checking for end-of-stream, exception handling, performance considerations (you don't want to perform things 1 byte at a time) etc. have been left out from this example.
I am trying to parse a DatagramPacket that I will receive at a socket. I know the format of the packet I will receive, which is a DHCPREQUEST packet, but I don't think that really matters. For simplicity's sake, let's just consider the first six fields:
First field is the "opcode", which is 1 byte.
Second field is the "hardware type" which is 1 byte.
Third, "hardware address length", 1 byte.
Fourth, "hops", 1 byte.
Fifth, "transaction identifier xid", 4 bytes.
Sixth, "seconds", 2 bytes.
After I receive the packet, my approach is to convert it to a byte array.
DatagramPacket request = new DatagramPacket(new byte[1024], 1024);
socket.receive(request);
byte[] buf = request.getData();
At this point, the packet is stored in the byte array buf as a series of bytes. Since I know what the structure of this byte sequence is, how can I parse it? The one-byte fields are simple enough, but how about the multiple-bit fields? For example, how can I extract bytes 4 to 7, and store them in a variable named xid?
I could manually put each byte into an array:
byte[] xid = new byte[4];
xid[0] = buf[4];
xid[1] = buf[5];
xid[2] = buf[6];
xid[3] = buf[7];
But that's just tedious, and impractical for fields that are hundreds of bytes in length. The String class can parse substrings given an offset and length; is there a similar method for byte arrays in Java?
Or am I somehow making things difficult for myself?
Wrap the byte array in a ByteArrayOutputStream; wrap a DataInputStream around that; then use the methods of DataInputStream.
The cleanest way to do something like this is probably to use the utility method Arrays.copyOfRange.
What you do is write yourself some helper methods to extract 2 byte, 4 byte, etc values from the packet, reading the bytes and assembling them into Java short, int or whatever values.
For example
public short getShort(byte[] buffer, int offset) {
return (short) ((buffer[offset] << 8) | buffer[offset + 1]);
}
Then you use these helper methods as often as you need to. (If you want to be fancy, you could have the methods update an attribute that holds the current position, so that you don't have to pass an offset argument.)
Alternatively, if you were not worried by the overheads, you could wrap the byte array in ByteArrayInputStream and a DataInputStream, and use the latter's API to read bytes, shorts, ints, and so on. IIRC, DataInputStream assumes that numbers are represented in the stream in "network byte order" ... which is almost certainly what the DHCP spec mandates.
I'm a bit late to this, but there's a ByteBuffer class:
ByteBuffer b = ByteBuffer.wrap(request.getData());
byte opcode = b.get();
byte hwtype = b.get();
byte hw_addr_len = b.get();
byte hops = b.get();
int xid = b.getInt();
short seconds = b.getShort();
Or, if you only need a single field:
ByteBuffer b = ByteBuffer.wrap(request.getData());
int xid = b.getInt(4);
I'm trying to send and receive a byte stream in which certain ranges of bytes represent different pieces of data. I've found ways to convert single primitive datatypes into bytes, but I'm wondering if there's a straightforward way to place certain pieces of data into specified byte regions.
For example, I might need to produce or read something like the following:
byte 1 - int
byte 2-5 - int
byte 6-13 - double
byte 14-21 - double
byte 25 - int
byte 26-45 - string
Any suggestions would be appreciated.
Try DataOutputStream/DataInputStream or, for arrays, the ByteBuffer class.
For storing the integer in X bytes, you may use the following method. If you think it is badly named, you may use the much less descriptive i2os name which is used in several (crypto) algorithm descriptions. Note that the returned octet string uses Big Endian encoding of unsigned ints, which you should specify for your protocol.
public static byte[] possitiveIntegerToOctetString(
final long value, final int octets) {
if (value < 0) {
throw new IllegalArgumentException("Cannot encode negative values");
}
if (octets < 1) {
throw new IllegalArgumentException("Cannot encode a number in negative or zero octets");
}
final int longSizeBytes = Long.SIZE / Byte.SIZE;
final int byteBufferSize = Math.max(octets, longSizeBytes);
final ByteBuffer buf = ByteBuffer.allocate(byteBufferSize);
for (int i = 0; i < byteBufferSize - longSizeBytes; i++) {
buf.put((byte) 0x00);
}
buf.mark();
buf.putLong(value);
// more bytes than long encoding
if (octets >= longSizeBytes) {
return buf.array();
}
// less bytes than long encoding (reset to mark first)
buf.reset();
for (int i = 0; i < longSizeBytes - octets; i++) {
if (buf.get() != 0x00) {
throw new IllegalArgumentException("Value does not fit in " + octets + " octet(s)");
}
}
final byte[] result = new byte[octets];
buf.get(result);
return result;
}
EDIT before storing the string, think of a padding mechanism (spaces would be most used), and character-encoding e.g. String.getBytes(Charset.forName("ASCII")) or "Latin-1". Those are the most common encodings with a single byte per character. Calculating the size of "UTF-8" is slightly more difficult (encode first, add 0x20 valued bytes at the end using ByteBuffer).
You may want to consider having a constant size for each data type. For example, the 32-bit Java int will take up 4 bytes a long will take 8, etc. In fact, if you use Java's DataInputStream and DataOutputStreams, you'll basically be doing that anyway. They have really nice methods like read/writeInt, etc.
I want to send integer values from my Arduino to a Java application. To do so, I am using a serial port. In the processing program for the Arduino I am printing the following to the serial port:
Serial.print(accel, DEC)
accel is a signed int. In my Java code I am implementing a SerialPortEventListener, and I am reading an array of bytes from the inputstream input.
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
int available = input.available();
byte[] chunk = new byte[available];
input.read(chunk);
System.out.println (new String(chunk));
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
I need to convert this array of bytes into integers, but I am not sure how to do this. The Arduino tutorial Lesson 4 - Serial communication and playing with data says that the Arduino stores signed ints in two bytes (16 bits). I have tried reading just two bytes of data into an array chunk and then decoding those two bytes into integer with the following code.
public synchronized void serialEvent(SerialPortEvent oEvent) {
if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
int available = input.available();
int n = 2;
byte[] chunk = new byte[n];
input.read(chunk);
int value = 0;
value |= chunk[0] & 0xFF;
value <<= 8;
value |= chunk[1] & 0xFF;
System.out.println(value);
} catch (Exception e) {
System.err.println(e.toString());
}
}
}
This should print out a stream of integers fluctuating between -512 and -516, but it doesn’t. The code prints out the following.
2949173
3211317
851978
2949173
3211317
851978
2949173
3211319
How can the bytes coming from the Arduino through the serial port to integers be decoded?
You can use ByteBuffer to convert byte to int, your code will look something like this:
ByteBuffer bb = ByteBuffer.wrap(chunk);
System.out.println(bb.getInt());
Edit: I should have recognized the ascii digits :-)
Why not: Make a line oriented protocol; Use a java BufferedReader and .readLine().
Parse the strings using Integer.valueOf(). Note that the newline is used as a
framing character.
It how most GPS GPS devices and all sorts of quite important devices are used.
Not optimal in any way, but there you go...
It turns out that the Serial.print() function on the Arduino converts the value to a string and sends the string to the serial port, which is why my bit shifting wasn't working. One solution is to collect the values in my java code as a string then and convert the string to an int.
However, to avoid the step of converting the values from string to int, you can use the serial.write() function to send ints to the serial port. To do this, in the Arduino sketch, define a union of a int and an array of bytes of the same size. Then create an instance of the union, set the integer, and Serial.write() the matching byte array. Collect the byte array in the Java program and do a bit shift to get the integer.
My Arduino sketch is coded like this:
union{
int i;
byte b[2];
}u;
u.i = accel;
Serial.write(u.b, 2);
My java code for reading the byte array and converting it to an int looks like this:
int n = 2;
byte[] chunk = new byte[n];
input.read(chunk);
short value = 0;
// get 2 bytes, unsigned 0..255
int low = chunk[0] & 0xff;
int high = chunk[1] & 0xff;
value = (short) (high << 8 | low) ;
System.out.println(value);
I am still looking for a way to get a float or double from the Arduino into the java code. I know I can write the float or double by replacing the int in the union as a double or float, set the double or float, and Serial.write() the matching byte array. My problem comes on the Java side. Once I have collected the byte array I have not found a way to correctly bit shift the data to the the float or double. Any help here?
My problem was similar. I used Arduino Serial.write to send a set of known int values (2 bytes/int). Arduino serial monitor correctly interpreted those values. However the java program got all the numbers wrong (except for zero). Apparently the byte order is reversed. I would expect that byte order is also the problem with other data types output by arduino. jSerialComm was used for reading the serial port. Here's the java code:
// declarations
byte[] byter = new byte[16];
ByteBuffer bufr = ByteBuffer.wrap(byter);
short[] shortr = new short[8];
// code
comPort.readBytes(byter, 16);
for(int i=0; i<8; i++){
shortr[i] = Short.reverseBytes(bufr.getShort(2*i));
}
I am coding some sort of packet which has different fields with different length in bytes.
So field1 is 2 byte long field2 is 3 bytes long field3 is 6 bytes long and when I addup these fields, I should be getting 11 bytes in length.
But I have no idea how I can declare something with this byte long.
Use an array:
byte[] byteArray = new byte[11];
How's about:
byte[] arr = new byte[11];
You could use a class to represent your packet:
public class Packet
{
public byte[] Field1, Field2, Field3;
public Packet(byte[] packetBytes)
{
ByteBuffer packet = ByteBuffer.wrap(packetBytes);
Field1 = new byte[2];
Field2 = new byte[3];
Field3 = new byte[6];
packet.get(Field1, 0, 2);
packet.get(Field2, 2, 3);
packet.get(Field3, 5, 6);
}
}
ByteBuffer is good for byte-manipulation.
I have found that java.nio.ByteBuffer is typically better for this sort of thing. It has nice methods for dealing with interpreting the bytes in the buffer. The docs are here.
import java.nio.ByteBuffer;
ByteBuffer buffer = ByteBuffer.allocate(11);
Check out the docs and look at the nice methods such as getInt() and getChar().
Java has a limited collection of primitive types, which all have a fixed size. You can see a list of them here. That means you can't decide how many bytes your variable will consist of.
Of course, as others have already mentioned, you can always create a new byte[11]. Note that Java's byte is signed, however. It goes from -128 to 127, not from 0 to 255.
I recommend the utility classes in Javolution for dealing with binary protocol streams such as this. They've come in handy for me several times when dealing with low-level binary streams.
You should probably design your code to separate the message you want to manipulate in java from the wire level format you need to read/write.
e.g. If you have a ScreenResolution concept, you could represent it in java with a ScreenResolution class:
public class ScreenResolution {
public int height;
public int width;
}
This class is easy to work with in Java. Transforming this to a packet that can be transmitted over a network/saved to a file, etc. according to some file format or protocol is another concern.
Say the height and width is to be laid out in 3 bytes each, with some ID and length for the "wire format", you make something like
public byte[] marshalScreenResolution(ScreenResolution obj) {
byte[] buf = new byte[9];
//length of this packet, 2 bytes
buf[0] = 0;
buf[1] = 9;
buf[2] = SCREENRESOLUTION_OPCODE;
//marshal the height/width , 3 least significant bytes.
buf[3] = (obj.height&0xff0000) >> 16;
buf[4] = (obj.height&0x00ff00) >> 8;
buf[5] = (obj.height&0x0000ff) ;
buf[6] = (obj.width&0xff0000) >> 16;
buf[7] = (obj.width&0x00ff00) >> 8;
buf[8] = (obj.width&0x0000ff) ;
return buf;
}
And you make a demarshalScreenResolution function for going from a packet to a ScreenResolution object. The point is you decouple the representation in java from the external representation, and you assemble the fields in the external representation using bytes + some basic bit fiddling.