【问题标题】:InputStream skips bytesInputStream 跳过字节
【发布时间】:2011-12-13 15:11:06
【问题描述】:

我第二次遇到 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 毫秒的睡眠来解决这个问题。它解决了问题,但我仍然很想知道更合适的解决方案

【问题讨论】:

  • 对我来说这听起来不太可能。流似乎更有可能真的包含数据。顺便说一句,您应该注意,如果您意外到达流的末尾,您将永远循环...您是否尝试过通过 Wireshark 查看网络数据?您是否对流进行了什么操作
  • 出于兴趣(类似于上面的问题);你怎么知道丢失的是 first 1182 个字节?
  • 所以这就像DataInput.readFully()
  • Jon -- 如果他们到达流的末尾,我认为 next 将第一次设置为 -1,但是他们会得到一个 EOFException,不是吗?虽然我同意,但 next 是 -1 应该是循环的结束条件。
  • @thomaatje:如果是这样,问题的原因可能在于读取这些先前数据的代码。它以某种方式消耗了图像的开头。

标签: java sockets inputstream


【解决方案1】:

您是否考虑过只发送图像数据(没有前面的图像大小)并使用外部库(例如 Apache Commons IO)为您处理这个问题?特别是,我认为您会发现IOUtils.toByteArray(InputStream input) 很有趣,例如

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

【讨论】:

  • 我一直在寻找这样的东西。但是在使用这样的库时,如果总大小未知,我怎么知道我什么时候收到了整个图像?
【解决方案2】:

我的猜测是BufferedReader 会假设你之后执行的任何读取操作都会通过它,所以它会很高兴地以缓冲区大小的增量来消耗输入。

您可以做的一件事是在input 之上使用BufferedInputStream,在此之上实例化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();

    }

}

【讨论】:

  • 感谢您的遮阳篷。一旦我可以再次访问我的代码,我将尝试此设置。看起来不错!
【解决方案3】:

正如名称**Buffererd**Reader 所表明的那样,它会从底层读取器的第一行中获取更多的字节,因此也会从流中获取。否则 if 不会被称为“缓冲”。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 2014-11-30
    • 2015-08-30
    • 1970-01-01
    相关资源
    最近更新 更多