I'm new to Java and currently doing some experiments on it.
I wrote a little program that does read and write stream of std I/O but I
kept getting exceptions thrown for out of range. Here is my code
int BLOCKSIZE = 128*1024;
InputStream inStream = new BufferedInputStream(System.in);
OutputStream outStream = new BufferedOutputStream(System.out);
byte[] buffer = new byte[BLOCKSIZE];
int bytesRead = 0;
int writePos = 0;
int readPos = 0;
while ((bytesRead = inStream.read(buffer,readPos,BLOCKSIZE)) != -1) {
outStream.write(buffer,writePos,BLOCKSIZE);
readPos += bytesRead;
writePos += BLOCKSIZE;
buffer = new byte[BLOCKSIZE];
}
Here is the exception thrown:"Exception in thread "main" java.lang.IndexOutOfBoundsException
at java.io.BufferedInputStream.read(BufferedInputStream.java:327)
at JavaPigz.main(JavaPigz.java:73)"
73th col is the inStream.read(...) statement. Basically I want to read 128kb bytes from stdin once and write it to the stdout and go back to read another 128kb chunk, so on and so forth. The same exception is also thrown to outStream.write()
I did some debugging and it looks BufferedInputStream buffers at most 64kb chunk once. Don't know if this is true. Thank you.
Edit: I also tried doing
InputStream inStream = new BufferedInputStream(System.in,BLOCKSIZE);
to specify the size of buffered chunk I want. But turns out it keeps giving size of 64kb
no matter what is specified
You're increasing your readPos (and writePos) in your loop. The subsequent reads are starting at that offset for inserting into your buffer, and attempting to write BLOCKSIZE bytes into it ... which won't fit, thus giving you an index out of bounds error.
The way you have that loop written, readPos and writePos should always be 0 especially since you're creating a new buffer every time. That being said ... you really don't want to do that, you want to re-use the buffer. It looks like you're just trying to read from the input stream and write it to the output stream ...
while ((bytesRead = inStream.read(buffer,readPos,BLOCKSIZE)) != -1) {
outStream.write(buffer,writePos,bytesRead);
}
your readPos and writePos correspond to the array ... not to the stream ...
set them 0 and leave them at 0
in your write call set param 3 to bytesRead instead of BLOCKSIZE
Related
I wrote a piece of Java code to send PDF-turned postscript scripts to a network printer via Socket.
The files were printed in perfect shape but every job comes with one or 2 extra pages with texts like ps: stack underflow or error undefined offending command.
At beginning I thought something is wrong with the PDF2PS process so I tried 2 PS files from this PS Files. But the problem is still there.
I also verified the ps files with GhostView. Now I think there may be something wrong with the code. The code does not throw any exception.
The printer, Toshiba e-studion 5005AC, supports PS3 and PCL6.
File file = new File("/path/to/my.ps");
Socket socket = null;
DataOutputStream out = null;
FileInputStream inputStream = null;
try {
socket = new Socket(printerIP, printerPort);
out = new DataOutputStream(socket.getOutputStream());
DataInputStream input = new DataInputStream(socket.getInputStream());
inputStream = new FileInputStream(file);
byte[] buffer = new byte[8000];
while (inputStream.read(buffer) != -1) {
out.write(buffer);
}
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
You are writing the whole buffer to the output stream regardless of how much actual content there is.
That means that when you write the buffer the last time it will most probably have a bunch of content from the previous iteration at the end of the buffer.
Example
e.g. imagine you have the following file and you use a buffer of size 10:
1234567890ABCDEF
After first inputStream.read() call it will return 10 and in the buffer you will have:
1234567890
After second inputStream.read() call it will return 6 and in the buffer you will have:
ABCDEF7890
After third inputStream.read() call it will return -1 and you will stop reading.
A printer socket will receive these data in the end:
1234567890ABCDEF7890
Here the last 7890 is an extra bit that the printer does not understand, but it can successfully interpret the first 1234567890ABCDEF.
Fix
You should consider the length returned by inputStream.read():
byte[] buffer = new byte[8000];
for (int length; (length = inputStream.read(buffer)) != -1; ){
out.write(buffer, 0, length);
}
Also consider using try-with-resources to avoid problems with unclosed streams.
I am acquiring thousands of TCP packets. I read them one packet after one packet but I want to read them as whole of 128 packets after 128 packets. For the moment, I use
s = new Socket(ip, port);
byte[] buffer = new byte[some_length];
stream = s.getInputStream();
stream.read(buffer);
Precisely, each ordered sequence of 128 packets corresponds to one image (that will be reconstructed afterwards). By the way, the first byte of each TCP packet corresponds to a number between 1 and 128, so that I can use these numbers as landmarks.
Is there a way, each time I get the first byte of a packet set to 1, to read those packets by sequence of 128 without having to code a dedicated loop (this loop would call 128 times stream.read(buffer);) ?
You state in the comments that every packet is exact 2048 bytes long, while the amount of this number isn't important, important is that the length is fixed.
There are different methods of reading fixed length packets:
Using InputStream.read in a loop
A call to InputStream.read may not fill the buffer fully, it may fill only 1 byte, even if you requested more. To counter this, you need to read in a while loop.
public byte[] readImage(InputStream in, int imageLength) throw IOException{
byte[] out = new byte[imageLength];
int read;
for(int i = 0; read = in.read(out, i, imageLength - i); i += read)
if(read < 0)
throw new EOFException();
return out;
}
In the loop above, we are first allocating a byte array of the required size, then we are calling in.read with our byte array and the current index. This way, we are sure we never return a half read packet to our caller
Using DataInput
Instead of manually reinventing the wheel, you can also use DataInput.readFully to read the byte array fully. This is easy:
byte[] image = new byte[imagelength];
DataInput in = new DataInputStream(inStream);
in.readFully(image);
here's how I proceed
DataInputStream dis = new DataInputStream(stream);
byte[] buffer = new byte[len];
while(buffer[0] !=1){
dis.readFully(buffer);
}
byte[] tmpBuffer = new byte[len];
byte[] finalBuffer = new byte[nb_line * len];
int count_lines = 0;
while(true){
dis.readFully(tmpBuffer);
System.arraycopy(tmpBuffer, 1, finalBuffer, (count_lines + 1) * rows, rows);
count_lines++;
if(count_lines == 127)
break;
}
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.
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.
The problem I am having is that when I use an InputStream to read bytes, it blocks until the connection is finished. EG:
InputStream is = socket.getInputStream();
byte[] buffer = new byte[20000];
while (is.read(buffer) != -1) {
System.out.println("reading");
}
System.out.println("socket read");
"socket read" doesn't print out until the FYN packet is actually recieved, thus closing the connection. What is the proper way to receive all the bytes in without blocking and waiting for the connection to drop?
Take a look at java.nio which has non-blocking IO support.
Reading till you get -1 means that you want to read until EOS. If you don't want to read until EOS, don't loop till the -1: stop sooner. The question is 'when?'
If you want to read a complete 'message' and no more, you must send the message in such a way that the reader can find its end: for example, a type-length-value protocol, or more simply a size word before each message, or a self-describing protocol such as XML.
With traditional sockets the point is that usually you do want them to block: what you do when logically you don't want your program to block is you put your reading/writing code in another thread, so that the separate read/write thread blocks, but not your whole program.
Failing that, you can use the available() method to see if there is actually any input available before reading. But then you need to be careful not to sit in a loop burning CPU by constantly calling available().
Edit: if the problem is that you're happy to block until the bytes have arrived, but not until the connection has dropped (and that is what is happeningh), then you need to make the client at the other end call flush() on its output stream after it has sent the bytes.
Try this:
InputStream is = socket.getInputStream();
byte[] buffer = new byte[20000];
int bytesRead;
do {
System.out.println("reading");
bytesRead = is.read(buffer);
}
while (is.available() > 0 && bytesRead != -1);
System.out.println("socket read");
More info: https://docs.oracle.com/javase/1.5.0/docs/api/java/io/InputStream.html#available()
Example taken from exampledepot on java.nio
// Create a direct buffer to get bytes from socket.
// Direct buffers should be long-lived and be reused as much as possible.
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
try {
// Clear the buffer and read bytes from socket
buf.clear();
int numBytesRead = socketChannel.read(buf);
if (numBytesRead == -1) {
// No more bytes can be read from the channel
socketChannel.close();
} else {
// To read the bytes, flip the buffer
buf.flip();
// Read the bytes from the buffer ...;
// see Getting Bytes from a ByteBuffer
}
} catch (IOException e) {
// Connection may have been closed
}
Be sure to understand buffer flipping because it causes a lot of headache. Basically, you have to reverse your buffer to read from it. If you are to reuse that buffer to have the socket to write in it, you have to flip it again. However clear() resets the buffer direction.
the code is probably not doing what you think it does.
read(buffer) returns the number of bytes it read, in other words: it is not guaranties to fill up your buffer anyway.
See DataInputStream.readFully() for code that fill up the entire array:
or you can use this functions (which are based on DataInputStream.readFully()) :
public final void readFully(InputStream in, byte b[]) throws IOException
{
readFully(in, b, 0, b.length);
}
public final void readFully(InputStream in, byte b[], int off, int len) throws IOException
{
if (len < 0) throw new IndexOutOfBoundsException();
int n = 0;
while (n < len)
{
int count = in.read(b, off + n, len - n);
if (count < 0) throw new EOFException();
n += count;
}
}
Your code would look like:
InputStream is = socket.getInputStream();
byte[] buffer = new byte[20000];
readFully(is, buffer);
System.out.println("socket read");