输入流跳过字节

发布于 2024-12-11 08:32:26 字数 1092 浏览 0 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

薄暮涼年 2024-12-18 08:32:27

我的猜测是 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();

    }

}

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

    }

}
愛放△進行李 2024-12-18 08:32:27

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

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

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);
乱了心跳 2024-12-18 08:32:26

正如名称 **Buffererd**Reader 所示,它将从底层读取器以及流中获取比第一行更多的字节。否则就不会被称为“缓冲”。

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

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文