【问题标题】:Java send screenshot over socketJava 通过套接字发送屏幕截图
【发布时间】:2019-04-14 21:29:44
【问题描述】:

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

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

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

屏幕截图通过了,但它被切碎了,只有大约 120-135kb 可以通过。在投票最多的答案中,他在关闭连接之前在客户端有Thread.sleep(),但我在没有它的情况下尝试了它,结果它被切碎了,所以我在他放的时候尝试了它,但它仍然被切碎了。我读取输入数据的客户端类处于循环状态,每 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();
    }
}
}

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

提前致谢

【问题讨论】:

    标签: java sockets screenshot serversocket


    【解决方案1】:

    您假设read() 填充了缓冲区。规范中没有这样说。试试这个:

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

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

    您也在发送端给自己带来困难。试试这个:

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

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

    事实上,当您在每个图像之后关闭连接时,您可以摆脱大小字和字节数组输入/输出流,只需直接从/到套接字执行ImageIO.read/write。这样会节省很多内存。

    【讨论】:

    • 我肯定会从这个变化中得到更好的结果。实际上,我将size 作为字节数组保存在发送端,size 也保存在读取端,我得到了大部分屏幕,但屏幕的右侧和底部没有被捕获。这可能与我使用 RDP 远程编程有关。非常感谢!
    • 考虑到空间和时间成本,我仍然会删除大小字和字节数组流。不要把简单的事情复杂化。
    • 呵呵...@EJP,我能问点什么吗? out.writeInt(大小); out.write(baos.toByteArray()); out.close();对我来说很好。除非我得到尺寸值,...我从哪里得到它?图像的 int 大小还是什么?
    • @gumuruh 你从baos.size()得到它,就像在OP的代码中一样。
    猜你喜欢
    • 2013-02-05
    • 2016-02-22
    • 1970-01-01
    • 1970-01-01
    • 2021-06-09
    • 2011-05-04
    • 1970-01-01
    • 2013-02-01
    相关资源
    最近更新 更多