InputStream пропускает байты - PullRequest
4 голосов
/ 23 октября 2011

Во второй раз у меня возникает эта чрезвычайно раздражающая проблема с InputStream.Этот InputStream принадлежит Сокету, который должен получить изображение.Код для чтения этого изображения следующий:

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);
    }
}

Теперь странно то, что этот код пропускает первые 1182 байта изображения, а затем читает оставшуюся часть.Поэтому, когда размер total равен 15000 байт, он читает 1182-15000 байт.

Я проверил Wireshark, и все изображение передается.Код не выдает никаких исключений.input.read() возвращает -1 как обычно.

Предыдущие данные были извлечены из потока с использованием BufferedReader.Эти данные имеют длину всего 5 символов, поэтому они не могут содержать пропущенный 1 КБ, но я предполагаю, что метод BufferedReader.readLine () считывает (буферизует) больше байтов из InputStream, чем необходимо.Может ли это быть правильным?

У меня была такая же проблема несколько месяцев назад, но я абсолютно не представляю, как я ее решил.

Надеюсь, кто-нибудь может помочь.

Заранее спасибо.

РЕДАКТИРОВАТЬ: Я могу решить проблему, добавив 100 мс сна между отправкой размера изображения и данных изображения.Это решает проблему, но я все еще хотел бы узнать более подходящее решение

Ответы [ 3 ]

3 голосов
/ 23 октября 2011

Как видно из названия **Buffererd**Reader, оно будет содержать больше байтов, чем просто первая строка из основного считывателя и, следовательно, также из потока.В противном случае, если бы его не называли «буферизованным».

К сожалению, я не знаю ни одного не устаревшего класса в Java, который позволял бы смешивать двоичные и текстовые данные так, как вы хотите.

Я предлагаю вам изменить ваш протокол и передать длину изображения также в некоторой двоичной кодировке.Тогда вы можете придерживаться InputStream.

0 голосов
/ 24 октября 2011

Рассматривали ли вы просто отправку данных изображения (без предыдущего размера изображения) и использование внешней библиотеки, такой как Apache Commons IO , чтобы справиться с этим для вас?В частности, я думаю, что вы найдете IOUtils.toByteArray (InputStream input) интересным, например,

InputStream input = socket.getInputStream();
byte[] buffer = IOUtils.toByteArray(input);
0 голосов
/ 23 октября 2011

Я предполагаю, что BufferedReader будет предполагать, что любые операции чтения, которые вы выполняете впоследствии, будут проходить через него, поэтому он с радостью будет использовать ввод с шагом в размере буфера.

Одна вещь, которую вы могли бы сделать, это использовать BufferedInputStream поверх input, создать экземпляр DataInputStream поверх этого для выполнения readLine(), а затем использовать BufferedInputStream в цикле. Документация говорит, что readLine устарела, потому что она не преобразует байты в символы должным образом, но я надеюсь, что с вашей первой строкой, содержащей только десятичные цифры, она не должна столкнуться с этой проблемой.

Я написал короткий тест, надеюсь, он охватит ваш вариант использования:

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();

    }

}
...