Thomas Smith
Thomas Smith

Reputation: 439

InputStream skips bytes

For the second time I have this extremely anoying problem with an InputStream. This InputStream belongs to a Socket that is supposed to receive an image. The code for reading this image is as below:

InputStream input = socket.getInputStream();

InputStreamReader reader = new InputStreamReader(input);
BufferedReader bufferedReader = new BufferedReader(reader);
int total = Integer.parseInt(bufferedReader.readLine());
int bytesRead = 0;
byte[] buffer = new byte[total]; // total is the total size of the image

while (bytesRead < total) {
    int next = input.read(buffer, bytesRead, total-bytesRead);
    if (next > 0) {
        bytesRead += next;
        System.out.println("Read: " + bytesRead);
    }
}

Now the strange thing is that this code skips the first 1182 bytes of the image, and then reads the remaining part. So when the total size is 15000 bytes, it reads byte 1182-15000.

I checked Wireshark and the whole image is transmitted. The code throws no exceptions. input.read() returns -1 as usual.

Pervious data has been readed from the stream using a BufferedReader. This data is only 5 characters long so it can't contain the missing 1K, but my guess is that the BufferedReader.readLine() method reads (buffers) more bytes from the InputStream than needed. Could this be correct?

I've had the same problem a few months ago but I absolutely have no clue on how I solved it.

Hope anyone can help.

Thanks in advance.

EDIT: I can solve the problem by adding a 100 ms sleep between sending the image size and the image data. It solves the problem but I would still realy like to know a more appropriate solution

Upvotes: 4

Views: 2793

Answers (3)

matsev
matsev

Reputation: 33759

Have you considered just sending the image data (without the preceding image size) and using an external library such as Apache Commons IO to handle this for you? In particular, I think that you will find IOUtils.toByteArray(InputStream input) interesting, e.g.

InputStream input = socket.getInputStream();
byte[] buffer = IOUtils.toByteArray(input);

Upvotes: 0

Vlad
Vlad

Reputation: 18633

My guess is the BufferedReader will assume that whatever reading operations you're performing afterwards will go through it, so it will happily consume input in increments of its buffer size.

One thing you could do is use a BufferedInputStream on top of input, instantiate a DataInputStream on top of that to do the readLine(), and then use the BufferedInputStream in your loop. The documentation says readLine is deprecated because it doesn't convert bytes to characters properly, but I'm hoping that with your first line containing only decimal digits, it shouldn't run into that problem.

I've written a short test, I hope it covers your use case:

import java.net.*;
import java.io.*;

class test{

    static byte[] b;

    static {
        b = new byte[123456];
        java.util.Random r = new java.util.Random();
        for (int i=0;i<b.length;i++)
            b[i] = (byte)(r.nextInt());
    }

    static void client() throws Exception{

        Socket socket = new Socket("127.0.0.1", 9000);

        InputStream input = socket.getInputStream();

        BufferedInputStream bis = new BufferedInputStream(input);

        //read in length
        DataInputStream dais = new DataInputStream(bis);
        int total = Integer.parseInt(dais.readLine());
        System.out.println("Total: "+total);

        int bytesRead = 0;
        byte[] buffer = new byte[total]; 

        while (bytesRead < total) {
            int next = bis.read(buffer, bytesRead, total-bytesRead);
            if (next > 0) {
                bytesRead += next;
                System.out.println("Read: " + bytesRead);
            }
        }

        for (int i=0;i<buffer.length;i++)
            if (buffer[i]!=b[i]){
                System.err.println("Error");
                System.exit(1);
            }
        System.out.println("OK");

        bis.close();
        socket.close();
    }

    static void server() throws Exception{
        ServerSocket srv = new ServerSocket(9000);

        Socket sock = srv.accept();

        OutputStream os = sock.getOutputStream();
        BufferedOutputStream bos =new BufferedOutputStream(os);
        DataOutputStream daos = new DataOutputStream(bos);

        //we're sending the b buffer

        //send the length in plain text, followed by newline
        byte[]num = String.valueOf(b.length).getBytes();
        daos.write(num,0,num.length);
        daos.write(10);
        //send actual buffer contents
        bos.write(b, 0, b.length);
        os.close();

        srv.close();
        sock.close();    

    }

    public static void main(String[]args){

        new Thread(new Runnable(){
            public void run(){
                try{server();}catch(Exception e){e.printStackTrace();}
            }
        }).start();

        new Thread(new Runnable(){
            public void run(){
                try{client();}catch(Exception e){e.printStackTrace();}
        }}).start();

    }

}

Upvotes: 0

A.H.
A.H.

Reputation: 66263

As the name **Buffererd**Reader indicates it will snarf more bytes than just the first line from the underlying reader and hence also from the stream. Otherwise if would not be called "buffered".

Unfortunately I'm not aware of any non-deprecated class in Java which allows mixing of binary and textual data in the way you want.

I suggest, that you modify your protocol and transfer the length of the image also in some binary encoding. Then you can stick to InputStream.

Upvotes: 3

Related Questions