Java tcp 只能检索一次图像 [英] Java tcp can only retrieve image once

查看:20
本文介绍了Java tcp 只能检索一次图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个从 tcp 服务器捕获屏幕截图的程序.它有效,但在一张屏幕截图后,我收到此错误:java.lang.IllegalArgumentException: image == null!.

I am making a program that captures a screenshot from a tcp server. It works but after one screenshot i get this error: java.lang.IllegalArgumentException: image == null!.

另外,我怎样才能使我的 tcp 客户端和服务器代码更健壮,因为这是我的第一个 tcp 项目,所以我知道我的代码很糟糕.这是我的代码:

Also how could i make my tcp client and servers code more robust as this is my first tcp project so i know that my code is pretty bad. Here is my code:

客户

package me.sanchixx.sss.client;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.Color;
import java.io.File;
import java.net.Socket;

import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.KeyStroke;

@SuppressWarnings("serial")
public class Interface extends JFrame implements ActionListener
{
    JPanel container = new JPanel(new BorderLayout());
    private final JMenuBar menuBar = new JMenuBar();
    private final JMenuBar menu = new JMenuBar();
    private final JMenu mnMenu = new JMenu("Menu");
    private final JMenuItem connect = new JMenuItem("Connect");
    private final JMenuItem screenshot = new JMenuItem("Screenshot");
    private final JMenuItem save = new JMenuItem("Save");
    ImageInPanel imgPan = new ImageInPanel();
    Socket skt = null;
    String ip;
    int port;
    static BufferedImage img = null;

    public Interface()
    {
        this.setSize(600, 600);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
        this.setTitle("Stupid Spying Shit");
        this.setResizable(true);
        this.setContentPane(container);
        this.setVisible(true);
        initComponents();
    }

    void initComponents()
    {
        setJMenuBar(menuBar);
        menuBar.add(mnMenu);
        connect.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK + ActionEvent.ALT_MASK));
        mnMenu.add(connect);
        screenshot.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
        mnMenu.add(screenshot);
        mnMenu.addSeparator();
        save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, ActionEvent.CTRL_MASK));
        mnMenu.add(save);
        menuBar.add(menu);
        imgPan.setBackground(new Color(0xffffff));
        container.add(imgPan);
        connect.addActionListener(this);
        screenshot.addActionListener(this);
    }

    public void actionPerformed(ActionEvent arg0) 
    {       
        if(arg0.getSource() == connect)
        {
            String adressGiven = JOptionPane.showInputDialog(null, "Server adress", "Prompt", JOptionPane.QUESTION_MESSAGE);
            if(adressGiven != null && adressGiven.length() != 0 && adressGiven.contains(":"))
            {
                String[] adress = adressGiven.split(":");
                ip = adress[0];
                port = Integer.parseInt(adress[1]);

                try 
                {
                    skt = new Socket(ip, port);
                }

                catch(Exception e) 
                {
                    JOptionPane.showMessageDialog(container, "Could not connect!", "Error", JOptionPane.ERROR_MESSAGE);
                }
            }

            else
            {
                JOptionPane.showMessageDialog(container, "Are you serious?", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }

        else if(arg0.getSource() == screenshot)
        {
            if (skt != null)
            {
                try 
                {
                    BufferedImage image = ImageIO.read(ImageIO.createImageInputStream(skt.getInputStream())); 
                    img = image;
                    System.out.print("Received image");
                    File outputfile = new File("c:/saved.png");
                    ImageIO.write(img, "jpg", outputfile);
                    repaint();
                }

                catch(Exception e) 
                {
                    e.printStackTrace();
                    JOptionPane.showMessageDialog(container, ":( " + e, "Error", JOptionPane.ERROR_MESSAGE);
                }
            }

            else
            {
                JOptionPane.showMessageDialog(container, "You are not connected to a server!", "Error", JOptionPane.ERROR_MESSAGE);
            }
        }
    }
}

服务器

package me.sanchixx.sss.server;

import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.net.ServerSocket;
import java.net.Socket;

import javax.imageio.ImageIO;

public class Main 
{
    public static void main(String args[]) throws Exception
    {
        @SuppressWarnings("resource")
        ServerSocket welcomeSocket = new ServerSocket(6789);

        while(true)
        {
            Socket skt = welcomeSocket.accept();
            System.out.print("Server has connected!");
            Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
            BufferedImage capture = new Robot().createScreenCapture(screenRect);
            ImageIO.write(capture, "jpg", skt.getOutputStream());
            /*try 
            {
                Thread.sleep(1000);
            } 

            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }*/
        }
    }
}

谢谢

推荐答案

通常您的服务器会接受传入的连接并生成一个新线程来处理该新套接字连接.

Normally your server would accept an incoming connection and spawn a new thread to handle that new socket connection.

然后该线程将继续循环(读/写),直到客户端断开连接.

That thread would then continue in a loop (reading/writing) until the client disconnected.

现在您可以在每次用户单击抓取"按钮时打开一个新连接、下载图像并关闭连接,但建立连接可能是一个耗时的过程.

Now you could open a new connection, download the image and close the connection each time the user clicks the "grab" button, but the establishment of the connection can be a time consuming process.

所以.基本思想是,当用户单击抓取"按钮时,客户端会向服务器发送抓取"屏幕截图的请求.服务器将生成屏幕截图并将其写回客户端.然后客户端将读取屏幕截图并显示它......简单......

So. The basic idea is, when the user clicks the "grab" button, the client will send a request to the server to "grab" a screen shot. The server will generate the screen shot and write it back to the client. The client will then read the screen shot and display it...simple...

只有一个小问题.ImageIO 的行为并不像你(和我)认为的那样.您不能简单地将图像写入套接字的 OutputStream 并使用套接字的 InputStream 读取它.似乎 ImageIO 需要关闭流,以便可以完成"......或其他东西.

There is just one little problem. ImageIO doesn't behave quite the way you (and I) think it should. You can't simply write the image out to the socket's OutputStream and read it using the socket's InputStream. It seems the ImageIO needs the stream to be closed so it can be "finalised" ... or something.

相反,我必须将它写入ByteArrayOutputStream,然后将生成的byte 数组写入套接字的OutputStream.相反,我必须将 byte 数组读入 ByteArrayOutputStream 并将其转储到 ByteArrayInputStream 以供 ImageIO...有趣的东西;)

Instead, I had to write it to a ByteArrayOutputStream, then write the resulting byte array to the socket's OutputStream. Conversely, I had to read the byte array into a ByteArrayOutputStream and the dump it into a ByteArrayInputStream to be read by ImageIO...fun stuff ;)

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.NumberFormat;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.event.IIOWriteProgressListener;

public class Server {

    public static void main(String args[]) {
        try {
            ServerSocket welcomeSocket = new ServerSocket(6789);

            while (true) {
                System.out.println("Get next client...");
                Socket skt = welcomeSocket.accept();
                // Hand of the processing to the socket handler...
                new Thread(new SocketHandler(skt)).start();
            }
        } catch (IOException ex) {
        }
    }

    // Reads a request from the client
    // All requests must be terminated with a new line (
)
    protected static String readRequest(InputStream is) throws IOException {
        StringBuilder sb = new StringBuilder(128);
        int in = -1;
        while ((in = is.read()) != '
') {
            sb.append((char) in);
        }
        return sb.toString();
    }

    // Grabs the screen shot and writes to the supplied output stream
    // This will first write the byte size of the following byte array and
    // writes the byte array of the image.  Clients should expect a 
    // int value terminated by a new line character (
)
    protected static void grabScreen(OutputStream os) throws AWTException, IOException {
        System.out.println("Grab screen shot");
        Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        BufferedImage capture = new Robot().createScreenCapture(screenRect);

        System.out.println("Writing image to buffer...");
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(capture, "jpg", baos);
        baos.close();
        System.out.println("Write byte size = " + baos.size());
        os.write((Integer.toString(baos.size()) + "
").getBytes());
        System.out.println("Write byte stream");
        os.write(baos.toByteArray());
        System.out.println("Image sent");
    }

    // Handler for an individual client socket...
    public static class SocketHandler implements Runnable {

        private Socket socket;

        public SocketHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            String request = null;
            InputStream is = null;
            OutputStream os = null;
            try {
                System.out.println("Processing client requests");
                is = socket.getInputStream();
                os = socket.getOutputStream();
                do {
                    System.out.println("Waiting for next request");
                    request = readRequest(is);
                    System.out.println("Request = " + request);
                    if ("grab".equalsIgnoreCase(request)) {
                        grabScreen(os);
                    }
                } while (!"done".equalsIgnoreCase(request) && !"shutdown".equalsIgnoreCase(request));
                System.out.println("Client has closed");
            } catch (IOException | AWTException exp) {
                exp.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (Exception e) {
                }
            }
            // Special command to stop the server...
            if ("shutdown".equalsIgnoreCase(request)) {
                System.exit(0);
            }
        }
    }
}

客户

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadProgressListener;
import javax.imageio.stream.ImageInputStream;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class Client {

    public static void main(String[] args) {
        new Client();
    }

    public Client() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                final CapturePane capturePane = new CapturePane();

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(capturePane);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        try {
                            capturePane.close();
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                });
                frame.setVisible(true);
            }
        });
    }

    public class CapturePane extends JPanel {

        private Socket socket;
        private ScreenPane screenPane;
        private JButton grabButton;

        public CapturePane() {
            setLayout(new BorderLayout());
            screenPane = new ScreenPane();
            grabButton = new JButton("Grab");
            try {
                socket = new Socket("localhost", 6789);
            } catch (IOException ex) {
                grabButton.setEnabled(false);
                ex.printStackTrace();
            }
            add(screenPane);
            add(grabButton, BorderLayout.SOUTH);

            grabButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (socket != null) {
                        InputStream is = null;
                        OutputStream os = null;

                        ByteArrayOutputStream baos = null;
                        ByteArrayInputStream bais = null;

                        try {
                            is = socket.getInputStream();
                            os = socket.getOutputStream();
                            // Send the "grab" request...
                            writeRequest(os, "grab");
                            System.out.println("Reading image...");
                            // Read back the expected byte size of the image
                            String size = readResponse(is);

                            int expectedByteCount = Integer.parseInt(size);
                            System.out.println("Expecting " + expectedByteCount);
                            // Create a buffer for the image bytes...
                            baos = new ByteArrayOutputStream(expectedByteCount);
                            byte[] buffer = new byte[1024];
                            int bytesRead = 0;
                            int bytesIn = 0;
                            // Read the image from the server...
                            while (bytesRead < expectedByteCount) {
                                bytesIn = is.read(buffer);
                                bytesRead += bytesIn;
                                baos.write(buffer, 0, bytesIn);
                            }
                            System.out.println("Read " + bytesRead);
                            baos.close();
                            // Wrap the result in an InputStream
                            bais = new ByteArrayInputStream(baos.toByteArray());

                            // Read the image...
                            BufferedImage image = ImageIO.read(bais);
                            System.out.println("Got image...");
                            screenPane.setImage(image);
                            bais.close();
                        } catch (IOException exp) {
                            exp.printStackTrace();
                        } finally {
                            try {
                                bais.close();
                            } catch (Exception exp) {
                            }
                            try {
                                baos.close();
                            } catch (Exception exp) {
                            }
                        }
                    }
                }

                protected String readResponse(InputStream is) throws IOException {
                    StringBuilder sb = new StringBuilder(128);
                    int in = -1;
                    while ((in = is.read()) != '
') {
                        sb.append((char) in);
                    }
                    return sb.toString();
                }

            });
        }

        protected void writeRequest(OutputStream os, String request) throws IOException {
            os.write((request + "
").getBytes());
            os.flush();
        }

        public void close() throws IOException {
            try {
                try {
                    System.out.println("Write done...");
                    writeRequest(socket.getOutputStream(), "shutdown");
                } finally {
                    try {
                        System.out.println("Close outputstream");
                        socket.getOutputStream().close();
                    } finally {
                        try {
                            System.out.println("Close inputStream");
                            socket.getInputStream().close();
                        } finally {
                            System.out.println("Close socket");
                            socket.close();
                        }
                    }
                }
            } finally {
                socket = null;
            }
        }

    }

    public class ScreenPane extends JPanel {

        private JLabel background;

        public ScreenPane() {
            setLayout(new BorderLayout());
            background = new JLabel();
            add(new JScrollPane(background));
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(400, 400);
        }

        public void setImage(BufferedImage img) {
            if (img != null) {
                ImageIcon icon = null;
                if (getWidth() > getHeight()) {
                    icon = new ImageIcon(img.getScaledInstance(getWidth(), -1, Image.SCALE_SMOOTH));
                } else {
                    icon = new ImageIcon(img.getScaledInstance(-1, getHeight(), Image.SCALE_SMOOTH));
                }
                background.setIcon(icon);
            } else {
                background.setIcon(null);
            }
            repaint();
        }

    }

}

未解决的问题、议题和主题

  1. 当前实现将仅捕获默认监视器.如果您有多个屏幕,则不会捕获它.这里有一个选择.捕获整个虚拟桌面或允许客户端指定要捕获的屏幕...
  2. 在事件调度线程中从服务器请求屏幕将导致客户端暂停".如果在本地运行,这可能不是问题,但如果您尝试远程执行此操作,这将是一个大问题.更好的解决方案是在后台进程中抓取和加载图像.SwingWorker 非常适合这个.查看Swing 中的并发了解更多详情
  1. The current implementation will only capture the default monitor. If you have more than one screen, this will not capture it. There's a choice here. Capture the entire virtual desktop or allow the client to specify the screen to capture...
  2. Requesting the screen from the server within the Event Dispatching Thread is going to cause the client to "pause". This might not be an issue if running locally, but is going to be a massive issue if your trying to do this remotely. A better solution would be to grab and load the image in a background process. SwingWorker is perfect for this. Check out Concurrency in Swing for more details

这篇关于Java tcp 只能检索一次图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆