使用Websocket实现html页面远程控制-(中转服务-傀儡端)(原理)
简介:远程控制,当网络互通时可直接将傀儡端(即被控制的一方)的信息发送到前端html页面,页面将命令直接发送傀儡端控制.但是在当网络不通时无法直接控制,需要中转服务来转发消息,傀儡端和控制端可以不在同一个网段,只需要都能和中转服务通信就能实现远程操控.本次只描述中转服务,和傀儡端的原理,都由Java语言实现.
远程操控平台介绍
该操作平台分为三个部分,控制端,中转服务(简称为服务器),傀儡端.
控制端:
- 控制其他PC,打开浏览器即可不需下载任何插件或应用即可进行远程操控,需要接受服务器发送的画面信息,并发送指令到中转服务.
傀儡端:
- 被控制的一方,由java实现,需要实时发送该pc的显示画面到服务器,并执行服务器转发到傀儡端的指令.
服务器
- 连接傀儡端和控制端,傀儡端和控制端可以不在一个网段,但只要都能和服务器连接就能进行远程操控,主要功能是接收傀儡端的画面信息转发到控制端,接收控制端的指令转发到傀儡端.
远程操控服务端实现原理简介
因为要与控制端html进行实时交互,所以采用websocket协议连接,发送JSON对象为信息载体,为了方便,傀儡端和服务端相连也采用websocket.
//传输对象
public class MessageDTO {
private String from; //发出本次信息的id
private String to; //需要接收这个信息的id
private TypeEnum type;//p-s c-s 类型傀儡端到服务器还是控制端到服务器
private String image; //图片信息
private AllEvent allEvent; //需要傀儡端执行的事件 包括鼠标事件和键盘事件等
}
服务端提供三个map分别存放客户端连接session(下文中简称clientMap),傀儡端连接session(下文简称puppetMap).以及客户端与傀儡端对应关系(clients_puppers).
- 当客户端连接根据url携带的参数将session放到clientMap中保存,将傀儡端和控制端关系放到clients_puppers中.
- 当傀儡端连接,根据url携带参数将session放到puppetMap.
/***
* @Author: yeguangrui
* @Date: 2020/12/3 11:31
* @param session 当前连接的会话
* @param from 从哪里来的
* @param to 如果是客户端 需要发送监控哪个id的动向 傀儡段不需要可以为任意值
* @param from 自己的唯一id
* @return: void
* 功能描述:
*/
@OnOpen
public void onOpen(Session session, @PathParam("type") String type, @PathParam("to") String to, @PathParam("from") String from) {
if (type.equals(TypeEnum.PUPPET_SERVER.toString())) {
puppets.put(from, session);
} else if (type.equals(TypeEnum.CLIENT_SERVER.toString())) {
clients.put(from, session);
clients_puppers.put(from, to);
} else {
log.error("type error");
}
}
- 当客户端发送来消息,根据参数携带的参数傀儡端id,从PuppetMap中取出该傀儡端与服务器连接的session将该信息发送给傀儡端,完成控制指令传输.
- 当傀儡端发送来消息,根据自身傀儡端id从relationMap中取出控制该傀儡端的客户端id,再用此id从clientMap中取出客户端与服务端连接的session,将画面信息用此session发送到傀儡端.
/**
* @Author: yeguangrui
* @Date: 2020/12/16 14:53
* @param message 接收到的信息
* @return: void
* 功能描述:接受信息,
* 如果是客户端来的 将信息发送给傀儡端执行,
* 如果是傀儡端来的遍历 对照关系map找到所有控制傀儡段的 客户端,发送图片信息
*/
@OnMessage
public void onMessage(String message) {
MessageDTO messageDTO = JSONObject.parseObject(message, MessageDTO.class);
//如果是客服端到服务器 ,发送给傀儡端
if (messageDTO.getType().equals(TypeEnum.CLIENT_SERVER)) {
String toId = messageDTO.getTo();
//log.info(toId);
this.sendMessageDTO(puppets.get(toId), messageDTO);
}
//如果是傀儡段到服务器 查出来控制该傀儡的所有客户端id并且发送该信息
else {
// puppertMessage.put(messageDTO.getFrom(), messageDTO);
for (String clientId : clients_puppers.keySet()) {
//获取傀儡段id和消息来源id相同
if (clients_puppers.get(clientId).equals(messageDTO.getFrom())) {
this.sendMessageDTO(clients.get(clientId), messageDTO);
}
}
}
}
- 当客户端断开连接,从clients_puppers和clientMap中删除该session的信息.
- 当傀儡端断开连接,从puppetMap和clients_puppers中删除该session信息.
远程操控傀儡端实现原理简介
傀儡端功能
- (通过websocket)实时发送自己的桌面信息到服务器再由服务器转发到前端页面,桌面信息为当前桌面截图(通过Java Robot类实现)
- 当接收到服务器的信息,解析传输对象,(通过Robot)执行事件对象中包含的信息(鼠标事件为哪个键,按压,释放),键盘事件为(哪个键,按压还是释放) 键盘事件对象:
public class MasterKeyEvent {
/**
* 按键状态
* TRUE.按下
* FALSE.释放
*/
private boolean pressed;
private Integer keyCode;
}
遇到的问题以及解决办法
- websocket传输图片过大超过了限制,需要自己实现ServletContextInitializer:
@Configuration
@ComponentScan
@EnableAutoConfiguration
public class WebAppRootContext implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(WebAppRootListener.class);
servletContext.setInitParameter("org.apache.tomcat.websocket.textBufferSize", "1024000");
}
}
- 图片过大传输慢,控制端展示慢,延迟高:使用谷歌的图片压缩thumbnailator压缩图片:
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.9</version>
</dependency>
- 鼠标问题:JavaRobot类截图会隐藏鼠标,需要自己绘制鼠标,但是绘制鼠标,前端页面不显示鼠标,延迟感会较为明显,采用控制端自己的鼠标会移动鼠标不会卡.
- 鼠标事件问题:当控制端发送ctrl,alt等键按下事件时前端会弹出相应win界面,导致前端监控不到相应的键释放事件,傀儡端当按下这些键时定时5s后自动执行释放事件.
- 傀儡端发送图片过于频繁问题,增加图片比较类,比较前一个图片与当前图片是否相同,相同则不发送,可以减少传输.
- 图片格式问题,采用其他图片格式较大,加载慢,后采用jpeg格式图片.
版权声明:本文为qq_42867665原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。