Handling binary packets - java

I coded this packet handler but I can imagine scenarios in which it will get stuck or won't be able to read incomplete data. My questions are:
Should I use two buffers, one for the current incoming data and other to append incomplete data to?
I'm being stupidly over-complicated?
Code:
byte[] buffer;
int bufferLength;
int bytesRead;
buffer = new byte[1024];
while (bluetoothConnected) {
try {
// Wait for packet header
if (mmInStream.available() >= 8) {
bufferLength = mmInStream.read(buffer);
bytesRead = 0;
// Parse every packet
while (true) {
int commandType = ByteBuffer.wrap(buffer, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();
int payloadSize = ByteBuffer.wrap(buffer, 2, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();
int packetSize = PACKET_HEADER_SIZE + payloadSize;
// Break if payload is incomplete
if (bufferLength < (bytesRead + packetSize)) {
// Append to other buffer
break;
}
byte[] packet = new byte[packetSize];
System.arraycopy(buffer, bytesRead, packet, 0, packetSize);
parsePacketSequence(socket, packet);
bytesRead += packetSize;
// Break if all bytes are read
if (bufferLength == bytesRead)
{
break;
}
// Break if more bytes are needed
// Packet header incomplete
if ((bufferLength - bytesRead) < PACKET_HEADER_SIZE)
{
// Append to other buffer
break;
}
}
}
}
catch (IOException e) {
bluetoothConnected = false;
Log.d(TAG, "Error " + e);
break;
}
}

Should I use two buffers, one for the current incoming data and other to append incomplete data to?
No.
I'm being stupidly over-complicated?
Yes.
Here's a simple version using DataInputStream:
DataInputStream din = new DataInputStream(mmInStream);
while (bluetoothConnected) {
try {
// Read packet header
int commandType = swap(din.readShort());
int payloadSize = swap(din.readShort());
int packetSize = PACKET_HEADER_SIZE + payloadSize;
byte[] packet = new byte[packetSize];
din.readFully(packet);
parsePacketSequence(socket, packet);
}
catch (IOException e) {
bluetoothConnected = false;
Log.d(TAG, "Error " + e);
break;
}
}
The swap() method which converts a short in litte-endian byte order to Java byte order is left as an exercise for the reader.
NB I don't see how parsePacketSequence() can work if it doesn't know commandType.
E&OE

Related

Using the OutputStream of the Bluetooth connection in an other thread disturbs receiving of messages

I followed the Android Guide to build a Bluetooth connection.
To separate things and make them independent, I decided to take the sending part of the BT to a separated thread. To achieve this, I pass the "OutStream" of the BT-Socket to a separated Thread class. My problem is, as soon as I start this thread, the incoming messages are not well red anymore.
But I don't know why, because I do not use this Thread at the moment. It is started but no messages are written in it.
This is part of the "ConnectedToDevice"-Class which receives the messages. I use a special way to detect that my Messages are received completely.
public void run() {
byte[] buffer = new byte[1024];
int bytes;
sendPW();
int len = 0;
Communication.getInstance().setFrequentSending(OVS_CONNECTION_IN_PROGRESS);
Communication.getInstance().setSendingMessages(mmOutStream); //Passing the OutStream to the sending class.
Communication.getInstance().setReceivingMessages(queueReceivingMsg);
Communication.getInstance().startThreads(); //currently: only start sending thread.
while (true) {
try {
bytes = this.mmInStream.read(buffer, len, buffer.length - len);
len += bytes;
if (len >= 3 && buffer[2] != -1) {
len = 0;
Log.d(TAG, "run: To Short?");
} else if (len >= 5) {
int dataLength = Integer
.parseInt(String.format("%02X", buffer[3]) + String.format("%02X", buffer[4]), 16);
if (len == 6 + dataLength) {
queueReceivingMsg.add(buffer);
Log.d(TAG, "run: Added to queue");
len = 0;
}
Log.d("BSS", "dataLenght: " + Integer.toString(dataLength) + " len " + len);
}
} catch (IOException var5) {
cancel();
Communication.getInstance().interruptThreads();
return;
}
}
}
The important part of sending message Class
public static BlockingQueue<Obj_SendingMessage> sendingMessages = new LinkedBlockingQueue<>();
#Override
public void run() {
while (!isInterrupted()) {
if (bGotResponse){
try{
sendingMessage = sendingMessages.take();
send(sendingMessage.getsData());
bGotResponse = false;
lTime = System.currentTimeMillis();
} catch (InterruptedException e){
this.interrupt();
}
}
if((System.currentTimeMillis()%500 == 0) && System.currentTimeMillis() <= lTime+1000){
if(sendingMessage != null){
send(sendingMessage.getsData());
}
} else {
bGotResponse =true;
}
}
}
//Where the outStream is used
private void write(int[] buffer) {
try {
for (int i : buffer) {
this.mmOutputStream.write(buffer[i]);
}
} catch (IOException var3) {
}
}
To be clear again, the sendingMessages is empty all the time, but still the messages get not Received correctly anymore.
Here's a proposal how robust code for reading messages from the stream could look like. The code can handle partial and multiple messages by:
Waiting for more data if a message is not complete
Processing the first message and saving the rest of the data if data for more than one message is available
Searching for a the marker byte 0xff and retaining the data for the next possibly valid message if invalid data needs to be discard
While writing this code I've noticed another bug in the code. If a message is found, the data is not copied. Instead the buffer is returned. However, the buffer and therefore the returned message might be overwritten by the next message before or while the previous one is processed.
This bug is more severe than the poor decoding of the stream data.
private byte[] buffer = new byte[1024];
private int numUnprocessedBytes = 0;
public void run() {
...
while (true) {
try {
int numBytesRead = mmInStream.read(buffer, numUnprocessedBytes, buffer.length - numUnprocessedBytes);
numUnprocessedBytes += numBytesRead;
processBytes();
} catch (IOException e) {
...
}
}
}
private void processBytes() {
boolean tryAgain;
do {
tryAgain = processSingleMessage();
} while (tryAgain);
}
private boolean processSingleMessage() {
if (numUnprocessedBytes < 5)
return false; // insufficient data to decode length
if (buffer[2] != (byte)0xff)
// marker byte missing; discard some data
return discardInvalidData();
int messageLength = (buffer[3] & 0xff) * 256 + (buffer[4] & 0xff);
if (messageLength > buffer.length)
// invalid message length; discard some data
return discardInvalidData();
if (messageLength > numUnprocessedBytes)
return false; // incomplete message; wait for more data
// complete message received; copy it and add it to the queue
byte[] message = Arrays.copyOfRange(buffer, 0, messageLength);
queueReceivingMsg.add(message);
// move remaining data to the front of buffer
if (numUnprocessedBytes > messageLength)
System.arraycopy(buffer, messageLength, buffer, 0, numUnprocessedBytes - messageLength);
numUnprocessedBytes -= messageLength;
return numUnprocessedBytes >= 5;
}
private boolean discardInvalidData() {
// find marker byte after index 2
int index = indexOfByte(buffer, (byte)0xff, 3, numUnprocessedBytes);
if (index >= 3) {
// discard some data and move remaining bytes to the front of buffer
System.arraycopy(buffer, index - 2, buffer, 0, numUnprocessedBytes - (index - 2));
numUnprocessedBytes -= index - 2;
} else {
// discard all data
numUnprocessedBytes = 0;
}
return numUnprocessedBytes >= 5;
}
static private int indexOfByte(byte[] array, byte element, int start, int end) {
for (int i = start; i < end; i++)
if (array[i] == element)
return i;
return -1;
}

Receiving truncated data while reading from Serial Port

I am trying to establish communication between Arduino and Android over Uart. So, while reading buffer on Android side I am not getting data in chunks.
if (uartDevice != null) {
// Loop until there is no more data in the RX buffer.
try {
byte[] buffer = new byte[CHUNK_SIZE];
int read;
while ((read = uartDevice.read(buffer, buffer.length)) > 0) {
data = new String(buffer, StandardCharsets.UTF_8).substring(0, read);
System.out.println(String.format("%020x", new BigInteger(1, data.getBytes(/*YOUR_CHARSET?*/))));
} catch (IOException e) {
Log.w(TAG, "Unable to transfer data over UART", e);
}
Expected output is:
2a3619010101001a0708403031301010011214084030313010100112140845
Instead I am receiving:
2a361a010101001a070840303130101001121408403031
8403031301010011214084030313010100112140845
3031301010011214084030313010100112140845
If you want to write code that only prints the bytes that you get I would try the following:
if (uartDevice != null) {
// Loop until there is no more data in the RX buffer.
try {
byte[] buffer = new byte[CHUNK_SIZE];
int read;
while ((read = uartDevice.read(buffer, buffer.length)) > 0) {
for (int i = 0; i < read; i++) {
System.out.printf("%02x", buffer[i]);
}
}
} catch (IOException e) {
Log.w(TAG, "Unable to transfer data over UART", e);
}
System.out.println(); // Adds a newline after all bytes
}
The following is a method that takes a UartDevice as a parameter, reads from it until the end and returns a single byte array with the whole content. No arbitrary buffer that is guaranteed to hold the whole content is needed. The returned array is exactly as big as it needs to be. Only a small read buffer is used to increase performance. Error handling is ignored.
This assumes that the data is not larger than it fits into memory.
byte[] readFromDevice(UartDevice uartDevice) {
byte[] buffer = new byte[CHUNK_SIZE];
int read;
ByteArrayOutputStream data = new ByteArrayOutputStream();
while ((read = uartDevice.read(buffer, buffer.length)) > 0) {
data.write(buffer, 0, read);
}
return data.toByteArray();
}
The method returns when all data has been read and you can process the returned array at your leasure.

Read all InputStream values at once into a byte[] array

Is there a way to read all InputStream values at once without a need of using some Apache IO lib?
I am reading IR signal and saving it from the InputStream into the byte[] array. While debugging, I have noticed that it works only if I put a delay there, so that I read all bytes at once and then process it.
Is there a smarter way to do it?
CODE:
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[100];
int numberOfBytes;
removeSharedPrefs("mSharedPrefs");
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
numberOfBytes = mmInStream.read(buffer);
Thread.sleep(700); //If I stop it here for a while, all works fine, because array is fully populated
if (numberOfBytes > 90){
// GET AXIS VALUES FROM THE SHARED PREFS
String[] refValues = loadArray("gestureBuffer", context);
if (refValues!=null && refValues.length>90) {
int incorrectPoints;
if ((incorrectPoints = checkIfGesureIsSameAsPrevious(buffer, refValues, numberOfBytes)) < 5) {
//Correct
} else {
//Incorrect
}
}
saveArray(buffer, numberOfBytes);
}else{
System.out.println("Transmission of the data was corrupted.");
}
buffer = new byte[100];
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, numberOfBytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Edit:
My old answer is wrong, see EJPs comment! Please don't use it. The behaviour of ByteChannels depend on wether InputStreams are blocking or not.
So this is why I would suggest, you just copy IOUtils.read from Apache Commons:
public static int read(final InputStream input, final byte[] buffer) throws IOException {
int remaining = buffer.length;
while (remaining > 0) {
final int location = buffer.length - remaining;
final int count = input.read(buffer, location, remaining);
if (count == -1) { // EOF
break;
}
remaining -= count;
}
return buffer.length - remaining;
}
Old answer:
You can use ByteChannels and read into a ByteBuffer:
ReadableByteChannel c = Channels.newChannel(inputstream);
ByteBuffer buf = ByteBuffer.allocate(numBytesExpected);
int numBytesActuallyRead = c.read(buf);
This read method is attempting to read as many bytes as there is remaining space in the buffer. If the stream ends before the buffer is fully filled, the number of bytes actually read is returned. See JavaDoc.

How to read all of Inputstream in Server Socket JAVA

I am using Java.net at one of my project.
and I wrote a App Server that gets inputStream from a client.
But some times my (buffered)InputStream can not get all of OutputStream that client sent to my server.
How can I write a wait or some thing like that, that my InputStream gets all of the OutputStream of client?
(My InputStream is not a String)
private Socket clientSocket;
private ServerSocket server;
private BufferedOutputStream outputS;
private BufferedInputStream inputS;
private InputStream inBS;
private OutputStream outBS;
server = new ServerSocket(30501, 100);
clientSocket = server.accept();
public void getStreamFromClient() {
try {
outBS = clientSocket.getOutputStream();
outputS = new BufferedOutputStream( outBS);
outputS.flush();
inBS = clientSocket.getInputStream();
inputS = new BufferedInputStream( inBS );
} catch (Exception e) {
e.printStackTrace();
}
}
Thanks.
The problem you have is related to TCP streaming nature.
The fact that you sent 100 Bytes (for example) from the server doesn't mean you will read 100 Bytes in the client the first time you read. Maybe the bytes sent from the server arrive in several TCP segments to the client.
You need to implement a loop in which you read until the whole message was received.
Let me provide an example with DataInputStream instead of BufferedinputStream. Something very simple to give you just an example.
Let's suppose you know beforehand the server is to send 100 Bytes of data.
In client you need to write:
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
try
{
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
while(!end)
{
int bytesRead = in.read(messageByte);
dataString += new String(messageByte, 0, bytesRead);
if (dataString.length == 100)
{
end = true;
}
}
System.out.println("MESSAGE: " + dataString);
}
catch (Exception e)
{
e.printStackTrace();
}
Now, typically the data size sent by one node (the server here) is not known beforehand. Then you need to define your own small protocol for the communication between server and client (or any two nodes) communicating with TCP.
The most common and simple is to define TLV: Type, Length, Value. So you define that every message sent form server to client comes with:
1 Byte indicating type (For example, it could also be 2 or whatever).
1 Byte (or whatever) for length of message
N Bytes for the value (N is indicated in length).
So you know you have to receive a minimum of 2 Bytes and with the second Byte you know how many following Bytes you need to read.
This is just a suggestion of a possible protocol. You could also get rid of "Type".
So it would be something like:
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
try
{
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
int bytesRead = 0;
messageByte[0] = in.readByte();
messageByte[1] = in.readByte();
int bytesToRead = messageByte[1];
while(!end)
{
bytesRead = in.read(messageByte);
dataString += new String(messageByte, 0, bytesRead);
if (dataString.length == bytesToRead )
{
end = true;
}
}
System.out.println("MESSAGE: " + dataString);
}
catch (Exception e)
{
e.printStackTrace();
}
The following code compiles and looks better. It assumes the first two bytes providing the length arrive in binary format, in network endianship (big endian). No focus on different encoding types for the rest of the message.
import java.nio.ByteBuffer;
import java.io.DataInputStream;
import java.net.ServerSocket;
import java.net.Socket;
class Test
{
public static void main(String[] args)
{
byte[] messageByte = new byte[1000];
boolean end = false;
String dataString = "";
try
{
Socket clientSocket;
ServerSocket server;
server = new ServerSocket(30501, 100);
clientSocket = server.accept();
DataInputStream in = new DataInputStream(clientSocket.getInputStream());
int bytesRead = 0;
messageByte[0] = in.readByte();
messageByte[1] = in.readByte();
ByteBuffer byteBuffer = ByteBuffer.wrap(messageByte, 0, 2);
int bytesToRead = byteBuffer.getShort();
System.out.println("About to read " + bytesToRead + " octets");
//The following code shows in detail how to read from a TCP socket
while(!end)
{
bytesRead = in.read(messageByte);
dataString += new String(messageByte, 0, bytesRead);
if (dataString.length() == bytesToRead )
{
end = true;
}
}
//All the code in the loop can be replaced by these two lines
//in.readFully(messageByte, 0, bytesToRead);
//dataString = new String(messageByte, 0, bytesToRead);
System.out.println("MESSAGE: " + dataString);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
You can read your BufferedInputStream like this. It will read data till it reaches end of stream which is indicated by -1.
inputS = new BufferedInputStream(inBS);
byte[] buffer = new byte[1024]; //If you handle larger data use a bigger buffer size
int read;
while((read = inputS.read(buffer)) != -1) {
System.out.println(read);
// Your code to handle the data
}
int c;
String raw = "";
do {
c = inputstream.read();
raw+=(char)c;
} while(inputstream.available()>0);
InputStream.available() shows the available bytes only after one byte is read, hence do .. while

Knowing How to Detect a Disconnection when Reading Socket Data (InputStream)

I have a thread that is constantly reading data from an InputStream. The InputStream data is coming from a Bluetooth socket. Previously, I wasn't using the if(mmInStream.available() > 0) around the InputStream read statement and when the bluetooth socket went away (someone turned off the device), the mmInStream.read would throw a IOException and then I could process my disconnection logic. What is the best way to determine when a disconnect has occurred?
First byte of 0xEE tells me its the leader of the data packet and the second tells me the length to read.
public void run() {
byte[] tempBuffer = new byte[1024];
byte[] buffer = null;
int byteRead=0;
long timeout=0;
long wait=100;
while (true) {
try {
timeout = System.currentTimeMillis() + wait;
if(mmInStream.available() > 0) {
while((mmInStream.available() > 0) && (tempBuffer[0] != (byte) 0xEE) && (System.currentTimeMillis() < timeout)){
byteRead = mmInStream.read(tempBuffer, 0, 1);
}
if(tempBuffer[0] == (byte) 0xEE){
timeout = System.currentTimeMillis() + wait;
while(byteRead<2 && (System.currentTimeMillis() < timeout)){
byteRead += mmInStream.read(tempBuffer, 1, 1);
}
}
timeout = System.currentTimeMillis() + wait;
while((byteRead<tempBuffer[1]) && (System.currentTimeMillis() < timeout)){
byteRead += mmInStream.read(tempBuffer, byteRead, tempBuffer[1]-byteRead);
}
}
if(byteRead > 0){
//do something with the bytes read in
}
}
catch (IOException e) {
bluetoothConnectionLost();
break;
}
}
}
You don't need all this malarkey with available(). Just set a read timeout with setSoTimeout, read, detect read returning -1, use the count returned by read if > 0 rather than assuming the buffer got filled, catch SocketTimeoutException to detect read timeouts, and catch IOException to detect other breakages.
After a look at the documentation, I think it's like this:
public void run() {
byte[] tempBuffer = new byte[1024];
int byteRead = 0;
while (true) {
try {
bytesRead = mmInStream.read(tempBuffer, 0, tempBuffer.length);
if (bytesRead < 0)
// End of stream.
break;
// Do something with the bytes read in. There are bytesRead bytes in tempBuffer.
} catch (IOException e) {
bluetoothConnectionLost();
break;
}
}
}
I think it's like this:
void fun(){
isOpen = true;
try{
InputStream stream = socket.getInputStream();
while(isOpen){
byte[] buf = new byte[8];
int pos = stream.read(buf);
if (pos < 0) {
throw new IOException();
}
//dosomething...
}
}catch(IOException e) {
isOpen = false;
}finally{
//do dispose here
}
}

Categories

Resources