Java tcp只能检索一次图像

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

另外我怎么能让我的tcp客户端和服务器代码更健壮,因为这是我的第一个tcp项目所以我知道我的代码非常糟糕。 这是我的代码:

客户

 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(); }*/ } } } 

谢谢

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

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

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

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

只有一个小问题。 ImageIO行为与你(和我)认为应该的方式不同。 您不能简单地将图像写入套接字的OutputStream并使用套接字的InputStream读取它。 似乎ImageIO需要关闭流,以便它可以“最终确定”……或者其他东西。

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

在此处输入图像描述在此处输入图像描述

服务器

 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 (\n) protected static String readRequest(InputStream is) throws IOException { StringBuilder sb = new StringBuilder(128); int in = -1; while ((in = is.read()) != '\n') { 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 (\n) 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()) + "\n").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()) != '\n') { sb.append((char) in); } return sb.toString(); } }); } protected void writeRequest(OutputStream os, String request) throws IOException { os.write((request + "\n").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中的Concurrency以获取更多详细信息