Socket / InputStream Dropping Data - java

I have been attempting to set up a basic server using Java's ServerSocket, Socket, and InputStream. In reading the InputStream, the expected result was a repeating series of byte 0x0b and 10 bytes of associated data (0x0b-data-0x0b-data repeating). The issue is that a small amount of the bytes are entirely dropped somewhere within the Java application, leaving only 9 bytes of data in some packets (after checking with Wireshark, the bytes are present in the original packets, just not the output of the InputStream).
The context in which this is happening is during a sequence of around a hundred packets sent in quick succession in response to certain behavior. I believe this is simply because there are more bytes that have an opportunity to be dropped and not the speed at which it is recieved.
After some searching, I found the same issue at Java Socket InputStream read missing bytes, but that thread died with requests for further information (and hence no useful answers).
The entirety of the code causing this problem is below. The most important sections are the while true loop and the readData function (excluding the else if chain).
To clarify, the question is the cause of this weird behaviour.
package com.kevycat.minerria;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
public class Minerria {
private static Socket client;
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(7777);
System.out.println("Listening");
client = socket.accept();
InputStream stream = client.getInputStream();
System.out.println("Connected");
byte[] extraData = new byte[0];
while (true) {
int available = stream.available();
byte[] data = new byte[available + extraData.length];
stream.read(data, extraData.length, available);
if (extraData.length > 0) {
for (int i = 0; i < extraData.length; i++) {
data[i] = extraData[i];
}
}
if (data.length > 0) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
System.out.println(" ");
}
if (data.length > 0) {
extraData = readData(data);
}
}
}
private static byte[] readData(byte[] data) throws IOException {
if (data.length < 3) {
return data;
}
int length = data[0] + data[1] * 256;
int type = data[2];
String payload = new String(Arrays.copyOfRange(data, 4, length));
System.out.println(length + " " + type + " " + payload);
if (type == 1) {
client.getOutputStream().write(new byte[] { 5, 0, 3, 0, 0 });
} else if (type == 4) {
client.getOutputStream().write(data);
} else if (type == 5) {
client.getOutputStream().write(data);
} else if (type == 68) {
client.getOutputStream().write(data);
} else if (type == 16) {
client.getOutputStream().write(data);
} else if (type == 42) {
client.getOutputStream().write(data);
} else if (type == 50) {
client.getOutputStream().write(data);
} else if (type == 6) {
byte[] b = new byte[80];
b[0] = 80;
b[2] = 7;
client.getOutputStream().write(b);
} else if (type == 8) {
client.getOutputStream().write(new byte[] { 11, 0, 9, 0, 1, 0, 0, 0, 'e', 'e', 'e' });
}
return data.length > length ? Arrays.copyOfRange(data, length, data.length - 1) : new byte[0];
}
}

int available = stream.available();
Don't do this. available() does nothing useful. If you don't believe me, I shall quote the javadoc:
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream.
'estimate'. That's programmer jargon. In plain english it translates as 'mostly useless'.
stream.read(data, extraData.length, available);
There's your error. You can't ignore the returned value of a read call. Read the javadoc: That read call will guarantee:
It reads at least 1 byte, unless the stream is closed / ended (then it reads nothing, and returns -1).
It will never read more than available.
But that is where it ends. It is perfectly legimitate for this method to only read half of available.
The ACTUAL # of bytes read is returned, unless it read nothing (only possible if stream is closed), then it returns -1.
The reason it's so convoluted is to get it to you as fast as possible. If packet arrives on your network card with 6 bytes and you ask for 10, it'll give you 6.
Use .readFully() if you want to just read X bytes (such as, 10 bytes, in your protocol that sounds useful), and ask the stream to wait as long as is needed (specifically, return only until either the stream ends are all 10 bytes are read).
For your protocol, I see two easy options:
Wrap the stream into a BufferedInputStream, and invoke only read(), the no-args one. That is a much simpler call: It returns -1 if stream ends, and a byte otherwise, easy peasy. It'll wait as long as needed until there's either data, or the stream is closed.
Alternatively, use .readFully. If you know that the data arrives in exact chunks of 11 every time, that'll work just as well. Although, calling a 'short' read (11 bytes is very short) on a non-buffered stream can be rather inefficient. Depends on the underlying stream.
Door #1 is less messy. It definitely does not suffer from inefficiency due to asking for too few bytes at a time, and it's hard to mess up your code.

Related

(Java NIO) Should I check both bytes read and buffer position in copying between channels?

This code is similar to example code in the JavaDoc of ByteBuffer.compact()
package com.study.nio;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
public class BufferCopy {
public static void main(String[] args) {
var from = "Hello World".getBytes();
var buf = ByteBuffer.allocate(5);
buf.clear();
try (var outputStream = new ByteArrayOutputStream();
var inChannel = Channels.newChannel(new ByteArrayInputStream(from));
var outChannel = Channels.newChannel(outputStream);) {
while (inChannel.read(buf) >= 0 || buf.position() != 0) {
buf.flip();
outChannel.write(buf);
buf.compact();
}
System.out.println(outputStream.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
My question is do we need the "|| buf.position() !=0" in the while condition check?
Just for reference, the code example provided in the documentation of Buffer.compact() is this:
buf.clear(); // Prepare buffer for use
while (in.read(buf) >= 0 || buf.position != 0) {
buf.flip();
out.write(buf);
buf.compact(); // In case of partial write
}
Scenario 1: Condition is Finally Met
Now, picture the moment when the channel in reaches its EOF and it adds its final 100 bytes to the buffer buff.
The code enters the while loop because in.read(buff) >= 0, the second condition is not even evaluated.
Now while writing out.write(buff) only 50 of the 100 bytes in the buffer are sent (perhaps the output channel has a very low bandwidth or its buffer is too small).
The buffer is compacted (pos=50,limit=100,cap=100).
Now, the while condition is evaluated again, this time in.read(buff) >= 0 is false because in channel is at its EOF, so it evaluates buf.position != 0 and we discover that there are still bytes in the buffer.
So, it goes back into the while loop and writes a few more bytes into the output channel.
Scenario 2: Slow Output Building up
Another way to look at how this condition gradually builds up is by picturing a scenario when the output channel is slower than the input channel, e.g. low bandwidth and/or a smaller output buffer.
The code enters the while loop because in.read(buff) >= 0, the second condition is not even evaluated. It reads 100 bytes.
Now while writing out.write(buff) only 50 of the 100 bytes in the buffer are sent.
The buffer is compacted (pos=50,limit=100,cap=100).
Now, the while condition is evaluated again, and this time in.read(buff) >= 0 adds 50 more bytes into the buffer and we are once again at 100 bytes total.
The while repeats in this fashion until the scenario 1 is reached once more buf.position != 0 is finally evaluated.

How to read (all available) data from serial connection when using JSSC?

I'm trying to work with JSSC.
I built my app according to this link:
https://code.google.com/p/java-simple-serial-connector/wiki/jSSC_examples
My event handler looks like:
static class SerialPortReader implements SerialPortEventListener {
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()){//If data is available
try {
byte buffer[] = serialPort.readBytes();
}
catch (SerialPortException ex) {
System.out.println(ex);
}
}
}
}
}
The problem is that I'm always not getting the incoming data in one piece. (I the message has a length of 100 bytes, Im getting 48 and 52 bytes in 2 separates calls)
- The other side send me messages in different lengths.
- In the ICD Im working with, there is a field which tell us the length of the message. (from byte #10 to byte #13)
- I cant read 14 bytes:
(serialPort.readBytes(14);,
parse the message length and read the rest of the message:
(serialPort.readBytes(messageLength-14);
But if I will do it, I will not have the message in once piece (I will have 2 separates byte[] and I need it in one piece (byte[]) without the work of copy function.
Is it possible ?
When working with Ethernet (SocketChannel) we can read data using ByteBuffer. But with JSSC we cant.
Is there a good alternative to JSSC ?
Thanks
You can't rely on any library to give you all the content you need at once because :
the library dont know how many data you need
the library will give you data as it comes and also depending on buffers, hardware, etc
You must develop your own business logic to handle your packets reception. It will of course depend on how your packets are defined : are they always the same length, are they separated with same ending character, etc.
Here is an example that should work with your system (note you should take this as a start, not a full solution, it doesn't include timeout for example) :
static class SerialPortReader implements SerialPortEventListener
{
private int m_nReceptionPosition = 0;
private boolean m_bReceptionActive = false;
private byte[] m_aReceptionBuffer = new byte[2048];
#Override
public void serialEvent(SerialPortEvent p_oEvent)
{
byte[] aReceiveBuffer = new byte[2048];
int nLength = 0;
int nByte = 0;
switch(p_oEvent.getEventType())
{
case SerialPortEvent.RXCHAR:
try
{
aReceiveBuffer = serialPort.readBytes();
for(nByte = 0;nByte < aReceiveBuffer.length;nByte++)
{
//System.out.print(String.format("%02X ",aReceiveBuffer[nByte]));
m_aReceptionBuffer[m_nReceptionPosition] = aReceiveBuffer[nByte];
// Buffer overflow protection
if(m_nReceptionPosition >= 2047)
{
// Reset for next packet
m_bReceptionActive = false;
m_nReceptionPosition = 0;
}
else if(m_bReceptionActive)
{
m_nReceptionPosition++;
// Receive at least the start of the packet including the length
if(m_nReceptionPosition >= 14)
{
nLength = (short)((short)m_aReceptionBuffer[10] & 0x000000FF);
nLength |= ((short)m_aReceptionBuffer[11] << 8) & 0x0000FF00;
nLength |= ((short)m_aReceptionBuffer[12] << 16) & 0x00FF0000;
nLength |= ((short)m_aReceptionBuffer[13] << 24) & 0xFF000000;
//nLength += ..; // Depending if the length in the packet include ALL bytes from the packet or only the content part
if(m_nReceptionPosition >= nLength)
{
// You received at least all the content
// Reset for next packet
m_bReceptionActive = false;
m_nReceptionPosition = 0;
}
}
}
// Start receiving only if this is a Start Of Header
else if(m_aReceptionBuffer[0] == '\0')
{
m_bReceptionActive = true;
m_nReceptionPosition = 1;
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
break;
default:
break;
}
}
}
After writing data to serial port it need to be flushed. Check the timing and pay attention to the fact that read should occur only after other end has written. read size is just an indication to read system call and is not guaranteed. The data may have arrived and is buffered in serial port hardware buffer but may not have been transferred to operating system buffer hence not to application. Consider using scm library, it flushes data after each write http://www.embeddedunveiled.com/
Try this:
Write your data to the serial port (using serialPort.writeBytes()) and if you are expecting a response, use this:
byte[] getData() throws SerialPortException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b;
try {
while ((b = serialPort.readBytes(1, 100)) != null) {
baos.write(b);
// System.out.println ("Wrote: " + b.length + " bytes");
}
// System.out.println("Returning: " + Arrays.toString(baos.toByteArray()));
} catch (SerialPortTimeoutException ex) {
; //don't want to catch it, it just means there is no more data to read
}
return baos.toByteArray();
}
Do what you want with the returned byte array; in my case I just display it for testing.
I found it works just fine if you read one byte at a time, using a 100ms timeout, and when it does time out, you've read all data in the buffer.
Source: trying to talk to an Epson serial printer using jssc and ESC/POS.

Input Stream only returning 1 byte

I am using java comm library to try accomplish a simple read/write to a serial port. I am able to successfully write to the port, and catch the return input from the input stream, but when I read from the input stream I am only able to read 1 byte (when I know there should be 11 returned)
I can write to the port successfully using Putty and am receiving the correct return String there. I am pretty new to Java, buffers and serial i/o and think maybe there is some obvious syntax or understanding of how data is returned to the InputStream. Could someone help me? Thanks!
case SerialPortEvent.DATA_AVAILABLE:
System.out.println("Data available..");
byte[] readBuffer = new byte[11];
try {
System.out.println("We trying here.");
while (inputStream.available() > 0) {
int numBytes = inputStream.read(readBuffer, 1, 11);
System.out.println("Number of bytes read:" + numBytes);
}
System.out.println(new String(readBuffer));
} catch (IOException e) {System.out.println(e);}
break;
}
This code returns the following output:
Data available..
We trying here.
Number of bytes read:1
U
As the documentation states
Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made to read as many as len bytes, but a smaller number may be read.
This behavior is perfectly legal. I would also expect that a SerialPortEvent.DATA_AVAILABLE does not guarantee that all data is available. It's potentially just 1 byte and you get that event 11 times.
Things you can try:
1) Keep reading until you have all your bytes. E.g. wrap your InputStream into a DataInputStream and use readFully, that's the simplest way around the behavior of the regular read method. This might fail if the InputStream does not provide any more bytes and signals end of stream.
DataInputStream din = new DataInputStream(in);
byte[] buffer = new byte[11];
din.readFully(buffer);
// either results in an exception or 11 bytes read
2) read them as they come and append them to some buffer. Once you have all of them take the context of the buffer as result.
private StringBuilder readBuffer = new StringBuilder();
public void handleDataAvailable(InputStream in) throws IOException {
int value;
// reading just one at a time
while ((value = in.read()) != -1) {
readBuffer.append((char) value);
}
}
Some notes:
inputStream.read(readBuffer, 1, 11)
Indices start at 0 and if you want to read 11 bytes into that buffer you have to specify
inputStream.read(readBuffer, 0, 11)
It would otherwise try to put the 11th byte at the 12th index which will not work.

RXTX java, inputStream does not return all the buffer

This is my code, I'm using rxtx.
public void Send(byte[] bytDatos) throws IOException {
this.out.write(bytDatos);
}
public byte[] Read() throws IOException {
byte[] buffer = new byte[1024];
int len = 20;
while(in.available()!=0){
in.read(buffer);
}
System.out.print(new String(buffer, 0, len) + "\n");
return buffer;
}
the rest of code is just the same as this, i just changed 2 things.
InputStream in = serialPort.getInputStream();
OutputStream out = serialPort.getOutputStream();
They are global variables now and...
(new Thread(new SerialReader(in))).start();
(new Thread(new SerialWriter(out))).start();
not exist now...
I'm sending this (each second)
Send(("123456789").getBytes());
And this is what i got:
123456789123
456789
123456789
1234567891
23456789
can anybody help me?
EDIT
Later, i got the better way to solve it. Thanks, this was the Read Code
public byte[] Read(int intEspera) throws IOException {
try {
Thread.sleep(intEspera);
} catch (InterruptedException ex) {
Logger.getLogger(COM_ClComunica.class.getName()).log(Level.SEVERE, null, ex);
}//*/
byte[] buffer = new byte[528];
int len = 0;
while (in.available() > 0) {
len = in.available();
in.read(buffer,0,528);
}
return buffer;
}
It was imposible for me to erase that sleep but it is not a problem so, thanks veer
You should indeed note that InputStream.available is defined as follows...
Returns an estimate of the number of bytes that can be read (or skipped over) from this input stream without blocking by the next invocation of a method for this input stream. The next invocation might be the same thread or another thread. A single read or skip of this many bytes will not block, but may read or skip fewer bytes.
As you can see, this is not what you expected. Instead, you want to check for end-of-stream, which is indicated by InputStream.read() returning -1.
In addition, since you don't remember how much data you have already read in prior iterations of your read loop, you are potentially overwriting prior data in your buffer, which is again not something you likely intended.
What you appear to want is something as follows:
private static final int MESSAGE_SIZE = 20;
public byte[] read() throws IOException {
final byte[] buffer = new byte[MESSAGE_SIZE];
int total = 0;
int read = 0;
while (total < MESSAGE_SIZE
&& (read = in.read(buffer, total, MESSAGE_SIZE - total)) >= 0) {
total += read;
}
return buffer;
}
This should force it to read up to 20 bytes, less in the case of reaching the end of the stream.
Special thanks to EJP for reminding me to maintain the quality of my posts and make sure they're correct.
Get rid of the available() test. All it is doing is telling you whether there is data ready to be read without blocking. That isn't the same thing as telling you where an entire message ends. There are few correct uses for available(), and this isn't one of them.
And advance the buffer pointer when you read. You need to keep track of how many bytes you have read so far, and use that as the 2nd parameter to read(), with buffer.length as the third parameter.

Trying to upload in chunks

I am trying to accomplish a large file upload on a blackberry. I am succesfully able to upload a file but only if I read the file and upload it 1 byte at a time. For large files I think this is decreasing performance. I want to be able to read and write at something more 128 kb at a time. If i try to initialise my buffer to anything other than 1 then I never get a response back from the server after writing everything.
Any ideas why i can upload using only 1 byte at a time?
z.write(boundaryMessage.toString().getBytes());
DataInputStream fileIn = fc.openDataInputStream();
boolean isCancel = false;
byte[]b = new byte[1];
int num = 0;
int left = buffer;
while((fileIn.read(b)>-1))
{
num += b.length;
left = buffer - num * 1;
Log.info(num + "WRITTEN");
if (isCancel == true)
{
break;
}
z.write(b);
}
z.write(endBoundary.toString().getBytes());
It's a bug in BlackBerry OS that appeared in OS 5.0, and persists in OS 6.0. If you try using a multi-byte read before OS 5, it will work fine. OS5 and later produce the behavior you have described.
You can also get around the problem by creating a secure connection, as the bug doesn't manifest itself for secure sockets, only plain sockets.
Most input streams aren't guaranteed to fill a buffer on every read. (DataInputStream has a special method for this, readFully(), which will throw an EOFException if there aren't enough bytes left in the stream to fill the buffer.) And unless the file is a multiple of the buffer length, no stream will fill the buffer on the final read. So, you need to store the number of bytes read and use it during the write:
while(!isCancel)
{
int n = fileIn.read(b);
if (n < 0)
break;
num += n;
Log.info(num + "WRITTEN");
z.write(b, 0, n);
}
Your loop isn't correct. You should take care of the return value from read. It returns how many bytes that were actually read, and that isn't always the same as the buffer size.
Edit:
This is how you usually write loops that does what you want to do:
OutputStream z = null; //Shouldn't be null
InputStream in = null; //Shouldn't be null
byte[] buffer = new byte[1024 * 32];
int len = 0;
while ((len = in.read(buffer)) > -1) {
z.write(buffer, 0, len);
}
Note that you might want to use buffered streams instead of unbuffered streams.

Categories