InputStream跳过字节

时间:2011-10-23 16:48:47

标签: java sockets inputstream

对于InputStream,我第二次遇到这个非常恼人的问题。 此InputStream属于应该接收图像的Socket。阅读此图像的代码如下:

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个字符,所以它不能包含丢失的1K,但我的猜测是BufferedReader.readLine()方法从InputStream读取(缓冲)更多的字节而不是需要的。这可能是正确的吗?

几个月前我遇到了同样的问题,但我完全不清楚我是如何解决它的。

希望任何人都可以提供帮助。

提前致谢。

编辑:我可以通过在发送图像大小和图像数据之间添加100毫秒的睡眠来解决问题。它解决了这个问题,但我仍然希望知道一个更合适的解决方案

3 个答案:

答案 0 :(得分:3)

由于名称**Buffererd**Reader表示它将比底层读取器中的第一行以及流中更多字节。否则,如果不被称为“缓冲”。

不幸的是,我不知道Java中有任何不推荐使用的类,它允许以你想要的方式混合二进制和文本数据。

我建议您修改协议并以一些二进制编码传输图像的长度。然后你可以坚持InputStream

答案 1 :(得分:0)

我的猜测是BufferedReader将假设您之后执行的任何读取操作都将通过它,因此它会很乐意以缓冲区大小的增量消耗输入。

您可以做的一件事是在BufferedInputStream之上使用input,在DataInputStream之上实例化readLine()以执行BufferedInputStream,然后使用{在你的循环中{1}}。文档说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();

    }

}

答案 2 :(得分:0)

您是否考虑过仅发送图像数据(没有前面的图像尺寸)并使用Apache Commons IO之类的外部库来为您处理此问题?特别是,我认为你会发现IOUtils.toByteArray(InputStream input)有趣,例如

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