【问题标题】:Sending a screenshot (bufferedImage) over a socket in java通过java中的套接字发送屏幕截图(bufferedImage)
【发布时间】:2013-02-05 17:15:16
【问题描述】:

我正在通过套接字发送一个 bufferedImage,我使用的是this 帖子中的示例:

发件人

   BufferedImage image = ....;
   ImageIO.write(image, "PNG", socket.getOutputStream());

接收者

   BufferedImage image = ImageIO.read(socket.getInputStream());

它有效 - 如果,并且仅当,我在此行之后关闭发件人的 outputStream:

 ImageIO.write(image, "PNG", socket.getOutputStream());

除了关闭 outputStream,我还能做些什么吗?

另外,我还能做些什么来避免完全使用 ImageIO?做任何事情似乎都需要很长时间。 另请注意,由于性能问题,应不惜一切代价避免以任何方式读取或写入硬盘。我需要尽可能快地进行此传输,(我正在试验并尝试创建一个类似于 VNC 的客户端,并将每个屏幕截图保存到硬盘会大大减慢一切)..

@乔恩斯基特

编辑 3:

发件人:(请注意,我发送的是 JPG 图像而不是 PNG)。

                    int filesize;
                    OutputStream out = c.getClientSocket().getOutputStream();

                    ByteArrayOutputStream bScrn = new ByteArrayOutputStream();
                    ImageIO.write(screenshot, "JPG", bScrn);
                    byte[] imgByte = bScrn.toByteArray();
                    bScrn.flush();
                    bScrn.close();

                    filesize = bScrn.size();
                    out.write(new String("#FS " + filesize).getBytes()); //Send filesize
                    out.write(new String("#<IM> \n").getBytes());        //Notify start of image

                    out.write(imgByte); //Write file
                    System.out.println("Finished");                                 

接收者:(input 是套接字输入流)

尝试 #1:

String str = input.toString();
imageBytes = str.getBytes();

InputStream in = new ByteArrayInputStream(imageBytes);
BufferedImage image = ImageIO.read(in);
in.close();

System.out.println("width=" + image.getWidth());

(失败:getWidth() 行出现 Nullpointer 异常) 我理解这个错误的意思是“损坏的图像”,因为它无法初始化它。对吗?

尝试 #2:

byte[] imageBytes = new byte[filesize];
for (int j = 0; i < filesize; i++)
{
    imageBytes[j] = (byte) input.read();
}

InputStream in = new ByteArrayInputStream(imageBytes);
BufferedImage image = ImageIO.read(in);
in.close();

System.out.println("width=" + image.getWidth());

(失败:getWidth() 行出现 Nullpointer 异常)

尝试 #3:

if (filesize > 0)
{

    int writtenBytes = 0;
    int bufferSize = client.getReceiveBufferSize();

    imageBytes = new byte[filesize];     //Create a byte array as large as the image
    byte[] buffer = new byte[bufferSize];//Create buffer


    do {
        writtenBytes += input.read(buffer); //Fill up buffer
        System.out.println(writtenBytes + "/" + filesize); //Show progress

        //Copy buffer to the byte array which will contain the full image
        System.arraycopy(buffer, 0, imageBytes, writtenBytes, client.getReceiveBufferSize());
        writtenBytes+=bufferSize;

    } while ((writtenBytes + bufferSize) < filesize);

    // Read the remaining bytes
    System.arraycopy(buffer, 0, imageBytes, writtenBytes-1, filesize-writtenBytes);
    writtenBytes += filesize-writtenBytes;
    System.out.println("Finished reading! Total read: " + writtenBytes + "/" + filesize);

}

InputStream in = new ByteArrayInputStream(imageBytes);
BufferedImage image = ImageIO.read(in);
in.close();

(失败:接收者给出:空指针异常)

尝试 4:

   int readBytes = 0;
   imageBytes = new byte[filesize];     //Create a byte array as large as the image



    while (readBytes < filesize)
    {
        readBytes += input.read(imageBytes);
    }

    InputStream in = new ByteArrayInputStream(imageBytes);
    BufferedImage image = ImageIO.read(in);
    in.close();

    System.out.println("width=" + image.getWidth());

(失败:发送方给出:java.net.SocketException:对等方重置连接:套接字写入错误)

尝试 #5:

使用 Jon skeet 的代码 sn-p,图像到达,但只是部分到达。我将它保存到一个文件 (1.jpg) 以查看发生了什么,它实际上发送了 80% 的图像,而文件的其余部分被空白填充。这会导致部分损坏的图像。这是我试过的代码:(注意 captureImg() 没有错,直接保存文件可以)

发件人:

    Socket s = new Socket("127.0.0.1", 1290);
    OutputStream out = s.getOutputStream();

    ByteArrayOutputStream bScrn = new ByteArrayOutputStream();
    ImageIO.write(captureImg(), "JPG", bScrn);
    byte imgBytes[] = bScrn.toByteArray();
    bScrn.close();

    out.write((Integer.toString(imgBytes.length)).getBytes());
    out.write(imgBytes,0,imgBytes.length);

收件人:

    InputStream in = clientSocket.getInputStream();
    long startTime = System.currentTimeMillis();

    byte[] b = new byte[30];
    int len = in.read(b);

    int filesize = Integer.parseInt(new String(b).substring(0, len));

    if (filesize > 0)
    {
        byte[] imgBytes = readExactly(in, filesize);
        FileOutputStream f = new FileOutputStream("C:\\Users\\Dan\\Desktop\\Pic\\1.jpg");
        f.write(imgBytes);
        f.close();

        System.out.println("done");

发送方仍然给对等方重置连接:套接字写入错误。 Click here for full sized image

【问题讨论】:

    标签: java sockets bufferedimage


    【解决方案1】:
    public static void main(String[] args) {
        Socket socket = null;
        try {
            DataInputStream dis;
            socket = new Socket("192.168.1.48",8000);
            while (true) {
                dis = new DataInputStream(socket.getInputStream());
                int len = dis.readInt();
                byte[] buffer = new byte[len];
                dis.readFully(buffer, 0, len);
                BufferedImage im = ImageIO.read(new ByteArrayInputStream(buffer));
                jlb.setIcon(new ImageIcon(im));
                jfr.add(jlb);
                jfr.pack();
                jfr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                jfr.setVisible(true);
                System.gc();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } 
        finally {
            try {
                socket.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    

    在 192.168.1.48:8000 机器 python 服务器中运行,我得到了 java 代码流

    【讨论】:

      【解决方案2】:

      对于像我这样的其他 StackOverflow 用户。

      在“Jon Skeet”的回答中。修改 readExactly 方法的以下行。

          <<original Line>>
          index += size;
          <<modified Line>>
          index += bytesRead;
      

      获取完整的图像数据。

      【讨论】:

        【解决方案3】:

        一种选择是将图像写入ByteArrayOutputStream,以便确定长度,然后先将该长度写入输出流。

        然后在接收端,你可以读取长度,然后将那么多字节读入一个字节数组,然后创建一个ByteArrayInputStream 来包装数组并将that 传递给ImageIO.read()

        在输出套接字正常关闭之前它不起作用,我并不感到完全惊讶 - 毕竟,包含有效 PNG 文件的文件 然后是其他东西 实际上并不是本身是有效的PNG文件,是吗?因此,阅读器需要在完成之前读取到流的末尾 - 而网络流的“结束”只有在连接关闭时才会出现。

        编辑:这是一种将给定字节数读入新字节数组的方法。作为单独的“实用程序”方法很方便。

        public static byte[] readExactly(InputStream input, int size) throws IOException
        {
            byte[] data = new byte[size];
            int index = 0;
            while (index < size)
            {
                int bytesRead = input.read(data, index, size - index);
                if (bytesRead < 0)
                {
                    throw new IOException("Insufficient data in stream");
                }
                index += size;
            }
            return data;
        }
        

        【讨论】:

        • @David:那是非常复杂的代码——特别是,你永远不应该捕获 ArrayIndexOutOfBoundsException。你为什么不直接使用out.write(imgByte)?在接收端,您使用的是client.getInputStream().read(buffer);,它完全忽略read 的返回值。不是一个好主意。调用getInputStream()一次,然后多次调用read,注意每次调用读取了多少数据。不要假设它总是与接收大小相同。
        • 是的,我忘了提。 out.write(imgByte) 以某种方式立即强制套接字关闭,并断开客户端。我不明白为什么会这样,但我认为你一次可以写入多少数据必须有限制,所以我尝试拆分并发送它。
        • @David:不,将数据写入套接字应该绝对没问题。您确定不是 receiving 端关闭了套接字吗?
        • @David:你仍然忽略了input.read() 的返回值。你不能那样做。您需要循环,计算出您还需要读取多少数据,并一次读取一个块。通过网络连接,您不会一口气搞定。
        • 只是这样做 - 编辑尝试 4. 接收方没有给出任何错误。然而,当我尝试一次写入超过 200kb 到输出流时,发送方只是断开连接并说“对等方重置连接”。有什么想法吗?
        猜你喜欢
        • 1970-01-01
        • 2016-02-22
        • 2011-12-23
        • 1970-01-01
        • 1970-01-01
        • 2021-06-09
        • 2011-05-04
        相关资源
        最近更新 更多