吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1776|回复: 14
收起左侧

[Java 原创] java通过网页监控屏幕添加鼠标支持

  [复制链接]
vsked 发表于 2025-10-13 17:31
在上一个版本中,我们通过API来调用键盘传输。
java通过网页监控屏幕并控制键盘
https://www.52pojie.cn/thread-2063450-1-1.html
(出处: 吾爱破解论坛)

在这个版本中,我们为远程监控软件实现反向控制功能,不仅可以查看屏幕,还可以进行鼠标操作哟,支持的鼠标操作有单击,双击,右击 操作。
想自己整合键盘或拖动可以自己试着修改下代码.
在这个版本中用了极少的代码就实现了java版本的websocket实时的远程屏幕监控,远程鼠标单击,远程鼠标双击,远程鼠标右击相关功能。
键盘操作API已经写好技术示例,下一步将会将键盘相关功能也整合进来。

先修改前台实现类,用js来捕捉鼠标相关操作,并封装成事件,通过websocket传到后台

[JavaScript] 纯文本查看 复制代码
"use strict"

class Index2 {

    baseWebsocketServerUrl = "ws://192.168.100.74:80/imageWebsocket";
    websocket = null;

    initUI() {
    }

    initEvent() {
        $("#runStateBt").on("click", () => {
            if ($("#runStateBt").html() === "开始") {
                console.log("hello");
                $("#runStateBt").html("停止");
                this.initWebsocketConnect();
            } else {
                console.log("bye");
                $("#runStateBt").html("开始");
                if (this.websocket) {
                    this.websocket.close();
                    this.websocket = null;
                }
            }
        });
        // 添加图片点击事件监听器
        $("#currentScreenVsk").on("click", (event) => {
            this.handleImageClick(event);
        });
        // 添加双击事件监听器
        $("#currentScreenVsk").on("dblclick", (event) => {
            this.handleImageDoubleClick(event);
        });

        // 添加右键点击事件监听器并阻止默认菜单
        $("#currentScreenVsk").on("contextmenu", (event) => {
            event.preventDefault();
            this.handleImageRightClick(event);
        });
    }

    initWebsocketConnect(){
        // 如果已有连接,先关闭
        if (this.websocket) {
            this.websocket.close();
        }

        // 创建WebSocket连接
        this.websocket = new WebSocket(this.baseWebsocketServerUrl);

        // 监听WebSocket连接打开
        this.websocket.onopen = (event) => {
            console.log("WebSocket连接已建立");
            // 连接建立后发送消息以启动服务端的数据发送
            this.websocket.send("start monitor");
        };

        // 监听WebSocket消息
        this.websocket.onmessage = (event) => {
            // 将接收到的数据更新到图片元素
            $("#currentScreenVsk").attr("src", event.data);
        };

        // 监听WebSocket连接关闭
        this.websocket.onclose = (event) => {
            console.log("WebSocket连接已关闭");
        };

        // 监听WebSocket错误
        this.websocket.onerror = (error) => {
            console.error("WebSocket错误:", error);
        };
    }

    handleImageClick(event) {
        const img = $("#currentScreenVsk")[0];
        const offsetX = event.offsetX;
        const offsetY = event.offsetY;
        const imgWidth = img.width;
        const imgHeight = img.height;

        // 计算相对于图片的坐标比例
        const xRatio = offsetX / imgWidth;
        const yRatio = offsetY / imgHeight;

        // 构造坐标数据对象
        const clickData = {
            type: "mouseClick",
            x: xRatio,
            y: yRatio,
            offsetX: offsetX,
            offsetY: offsetY,
            imgWidth: imgWidth,
            imgHeight: imgHeight
        };

        // 通过WebSocket发送坐标数据到后台
        if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
            this.websocket.send(clickData.type+","+clickData.x+","+clickData.y);
        } else {
            console.warn("WebSocket未连接,无法发送点击坐标");
        }
    }

    handleImageDoubleClick(event) {
        const img = $("#currentScreenVsk")[0];
        const offsetX = event.offsetX;
        const offsetY = event.offsetY;
        const imgWidth = img.width;
        const imgHeight = img.height;

        // 计算相对于图片的坐标比例
        const xRatio = offsetX / imgWidth;
        const yRatio = offsetY / imgHeight;

        // 构造双击事件数据对象
        const clickData = {
            type: "mouseDoubleClick",
            x: xRatio,
            y: yRatio,
            offsetX: offsetX,
            offsetY: offsetY,
            imgWidth: imgWidth,
            imgHeight: imgHeight
        };

        // 通过WebSocket发送坐标数据到后台
        if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
            this.websocket.send(clickData.type+","+clickData.x+","+clickData.y);
        } else {
            console.warn("WebSocket未连接,无法发送双击坐标");
        }
    }

    handleImageRightClick(event) {
        const img = $("#currentScreenVsk")[0];
        const offsetX = event.offsetX;
        const offsetY = event.offsetY;
        const imgWidth = img.width;
        const imgHeight = img.height;

        // 计算相对于图片的坐标比例
        const xRatio = offsetX / imgWidth;
        const yRatio = offsetY / imgHeight;

        // 构造右键点击事件数据对象
        const clickData = {
            type: "mouseRightClick",
            x: xRatio,
            y: yRatio,
            offsetX: offsetX,
            offsetY: offsetY,
            imgWidth: imgWidth,
            imgHeight: imgHeight
        };

        // 通过WebSocket发送坐标数据到后台
        if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
            this.websocket.send(clickData.type+","+clickData.x+","+clickData.y);
        } else {
            console.warn("WebSocket未连接,无法发送右键点击坐标");
        }
    }

}

$(document).ready(function () {
    const index = new Index2();
    index.initUI();
    index.initEvent();
});


再通过后台java代码处理websocket接收到的鼠标坐标与操作类型,然后使用robot类,先获取到屏幕宽与高再乘以点击的坐标,这样就还原点击的位置了,详细代码如下。

[Java] 纯文本查看 复制代码
package com.vsked.web;

import com.vsked.remote.GlobalObj;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.awt.event.InputEvent;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

@Component
@ServerEndpoint("/imageWebsocket")
public class ImageWebSocket {

    private static final Logger log = LoggerFactory.getLogger(ImageWebSocket.class);

    private static List<Session> sessions = new CopyOnWriteArrayList<>();
    private static volatile boolean sending = false;
    private static Thread sendingThread = null;

    @OnOpen
    public void onOpen(Session session) {
        log.info("New connection: {}", session.getId());
        sessions.add(session);
    }

    @OnClose
    public void onClose(Session session) {
        log.info("Connection closed: {}", session.getId());
        Iterator<Session> it = sessions.iterator();
        Session s;
        while (it.hasNext()) {
            s = it.next();
            if (s.getId().equals(session.getId())) {
                it.remove();
            }
        }

        // 如果没有会话了,停止发送数据
        if (sessions.isEmpty()) {
            sending = false;
            if (sendingThread != null) {
                sendingThread.interrupt();
            }
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("Error: {}", throwable.getMessage());
    }

    @OnMessage
    public void onMessage(Session session, String message) {
        log.info("Received message: {}", message);

        double baseWith=GlobalObj.getRe().getWidth();
        double baseHeight=GlobalObj.getRe().getHeight();

        boolean isMouseEvent = false;

        if(message.contains("mouseClick")){
            try {
                String[] arr=message.split(",");
                int myX= (int) (baseWith*Double.parseDouble(arr[1]));
                int myY= (int) (baseHeight*Double.parseDouble(arr[2]));
                GlobalObj.getRobot().mouseMove( myX,myY);
                GlobalObj.getRobot().mousePress(InputEvent.BUTTON1_DOWN_MASK);
                GlobalObj.getRobot().mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                isMouseEvent = true;
            } catch (Exception e) {
                log.error("Error processing mouseClick: {}", e.getMessage());
            }
        }

        if(message.contains("mouseRightClick")){
            try {
                String[] arr=message.split(",");
                int myX= (int) (baseWith*Double.parseDouble(arr[1]));
                int myY= (int) (baseHeight*Double.parseDouble(arr[2]));
                GlobalObj.getRobot().mouseMove(myX,myY);
                GlobalObj.getRobot().mousePress(InputEvent.BUTTON3_DOWN_MASK);
                GlobalObj.getRobot().mouseRelease(InputEvent.BUTTON3_DOWN_MASK);
                isMouseEvent = true;
            } catch (Exception e) {
                log.error("Error processing mouseRightClick: {}", e.getMessage());
            }
        }

        if(message.contains("mouseDoubleClick")){
            try {
                String[] arr=message.split(",");
                int myX= (int) (baseWith*Double.parseDouble(arr[1]));
                int myY= (int) (baseHeight*Double.parseDouble(arr[2]));
                GlobalObj.getRobot().mouseMove(myX,myY);
                GlobalObj.getRobot().mousePress(InputEvent.BUTTON1_DOWN_MASK);
                GlobalObj.getRobot().mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                GlobalObj.getRobot().delay(50);  // 短暂延迟,模拟真实双击
                GlobalObj.getRobot().mousePress(InputEvent.BUTTON1_DOWN_MASK);
                GlobalObj.getRobot().mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
                isMouseEvent = true;
            } catch (Exception e) {
                log.error("Error processing mouseDoubleClick: {}", e.getMessage());
            }
        }

        // 如果是鼠标事件,处理完后继续执行发送任务逻辑
        if (isMouseEvent) {
            log.info("Mouse event processed, continuing with image sending,{}",sending);
            sending=false;
        }

        // 如果已经在发送数据,则停止之前的发送任务
        if (sending) {
            sending = false;
            if (sendingThread != null) {
                try {
                    sendingThread.join(); // 等待线程结束
                } catch (InterruptedException e) {
                    log.error("Thread join interrupted: {}", e.getMessage());
                }
            }
        }

        // 启动新的发送任务
        sending = true;
        sendingThread = new Thread(() -> {
            while (sending) {
                try {
                    for (Session s : sessions) {
                        try {
                            if(s.isOpen()){
                                s.getBasicRemote().sendText(GlobalObj.getImg());
                            }else {
                                //如果会话已关闭,则从sessions中移除
                                sessions.remove(s);
                            }

                        } catch (Exception e) {
                            log.error("Error sending message: {}", e.getMessage());
                        }
                    }
//                    Thread.sleep(1500); // 每隔1.5秒发送一次
                } catch (Exception e) {
                    log.info("Sending thread interrupted");
                    break;
                }
            }
        });
        sendingThread.start();
    }

}


测试的时候请先修改下index2.js里局域网IP地址,并用另外一台电脑测试这样更好一点,毕竟远程监控嘛,控制当然是另一台电脑啦。
你也可以到github上去下载最新版本
https://github.com/brucevsked/vskedremote

详细代码在附件中
vskedremote.zip (61.51 KB, 下载次数: 10)


免费评分

参与人数 2吾爱币 +11 热心值 +2 收起 理由
Carinx + 1 + 1 用心讨论,共获提升!
hrh123 + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

wjw0716 发表于 2025-10-13 17:39
用websocket的话,长时间好像会断,得考虑一下重连
yzqdev 发表于 2025-10-14 09:01
jiang01258 发表于 2025-10-13 19:34
 楼主| vsked 发表于 2025-10-14 07:57
jiang01258 发表于 2025-10-13 19:34
这个是不是就是远程控制啊

是的,只需要发一个网址,你的朋友就可以用鼠标操作你的电脑啦,不需要安装任何客户端哟
Kls673M 发表于 2025-10-14 09:13
有演示效果吗?  如果能通过网页授权控制对方电脑那确实挺方便的,不用安装那么麻烦而且还受软件的性能影响

像https://www.52pojie.cn/thread-1680516-1-1.html这个在网页控制手机、电脑就很方便了
2025crj 发表于 2025-10-14 10:13
思路可以  试试看
 楼主| vsked 发表于 2025-10-14 10:39
Kls673M 发表于 2025-10-14 09:13
有演示效果吗?  如果能通过网页授权控制对方电脑那确实挺方便的,不用安装那么麻烦而且还受软件的性能影响 ...

老版本里有网友运行起来图可以看下效果
巧用java编写远程桌面查看软件
https://www.52pojie.cn/thread-2062822-1-1.html
(出处: 吾爱破解论坛)
 楼主| vsked 发表于 2025-10-14 10:39
Kls673M 发表于 2025-10-14 09:13
有演示效果吗?  如果能通过网页授权控制对方电脑那确实挺方便的,不用安装那么麻烦而且还受软件的性能影响 ...

https://www.52pojie.cn/forum.php?mod=redirect&goto=findpost&ptid=2062822&pid=53940808
这个帖子,可以看到运行的效果
witkidpro001 发表于 2025-10-15 08:48
这是自己写了一个todesk么?
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-10 08:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表