Отправка снимка экрана (bufferedImage) через сокет в Java - PullRequest
8 голосов
/ 07 августа 2011

Я отправляю bufferedImage через сокет, и я использую пример, найденный в этом сообщении:

Отправитель

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

(ошибка: исключение Nullpointer в строке getWidth ()) Я понимаю, что эта ошибка означает «испорченное изображение», потому что она не может его инициализировать. исправить?

Попытка № 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());

(ошибка: исключение Nullpointer в строке getWidth ())

Попытка № 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:

Используя фрагмент кода Джона Скита, изображение прибывает, но только частично. Я сохранил его в файл (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");

Отправитель по-прежнему выдает сброс соединения по адресу peer: ошибка записи в сокет. Нажмите здесь для полноразмерного изображения Problem

Ответы [ 3 ]

8 голосов
/ 07 августа 2011

Один из вариантов - записать изображение в ByteArrayOutputStream, чтобы вы могли определить длину, а затем сначала записать эту длину в выходной поток.

Затем на принимающей стороне вы можете прочитать длину, затем прочитайте так много байтов в байтовый массив, затем создайте ByteArrayInputStream, чтобы обернуть массив и передать , что в 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;
}
1 голос
/ 24 марта 2014

для других пользователей StackOverflow, таких как я.

В ответе Джона Скита. Измените следующую строку метода readExactly.

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

Для получения полных данных изображения.

0 голосов
/ 20 июля 2016
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

...