├── .gitignore ├── puppet ├── src │ ├── main │ │ ├── java │ │ │ ├── META-INF │ │ │ │ └── MANIFEST.MF │ │ │ └── cn │ │ │ │ └── yang │ │ │ │ └── puppet │ │ │ │ └── client │ │ │ │ ├── constant │ │ │ │ ├── PuppetDynamicSetting.java │ │ │ │ ├── MessageConstants.java │ │ │ │ ├── ConfigConstants.java │ │ │ │ └── ExceptionMessageConstants.java │ │ │ │ ├── exception │ │ │ │ ├── HeartBeatException.java │ │ │ │ ├── NullValueException.java │ │ │ │ └── PuppetClientException.java │ │ │ │ ├── commandhandler │ │ │ │ ├── ControlCommandHandler.java │ │ │ │ ├── TerminateCommandHandler.java │ │ │ │ ├── QualityCommandHandler.java │ │ │ │ ├── KeyBoardEventCommandHandler.java │ │ │ │ ├── MouseEventCommandHandler.java │ │ │ │ └── AbstractPuppetCommandHandler.java │ │ │ │ ├── ui │ │ │ │ ├── impl │ │ │ │ │ └── PuppetDesktop.java │ │ │ │ ├── MessageDialog.java │ │ │ │ └── IReplay.java │ │ │ │ ├── PuppetStarter.java │ │ │ │ ├── netty │ │ │ │ ├── PuppetNettyClientHandler.java │ │ │ │ └── PuppetNettyClient.java │ │ │ │ └── robot │ │ │ │ ├── JavaRobotReplay.java │ │ │ │ └── GoRobotReplay.java │ │ ├── resources │ │ │ ├── log4j.properties │ │ │ ├── META-INF │ │ │ │ └── commandhandlers │ │ │ ├── puppet-config.txt │ │ │ └── puppet-beans.xml │ │ └── proto │ │ │ └── go_robot.proto │ └── test │ │ └── java │ │ └── GoRobotReplayTest.java ├── dependency-reduced-pom.xml ├── pom.xml └── puppet.iml ├── .idea ├── markdown-navigator │ └── profiles_settings.xml ├── vcs.xml ├── misc.xml ├── libraries │ ├── Maven__log4j_log4j_1_2_17.xml │ ├── Maven__org_objenesis_objenesis_2_1.xml │ ├── Maven__org_slf4j_slf4j_api_1_7_25.xml │ ├── Maven__org_slf4j_slf4j_log4j12_1_7_25.xml │ ├── Maven__net_coobird_thumbnailator_0_4_8.xml │ ├── Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml │ ├── Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml │ ├── Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml │ ├── Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml │ ├── Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml │ ├── Maven__org_springframework_spring_context_5_0_6_RELEASE.xml │ ├── Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml │ └── Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml ├── modules.xml ├── compiler.xml ├── artifacts │ ├── desktop_control_master.xml │ └── desktop_control_puppet.xml └── markdown-navigator.xml ├── master ├── src │ └── main │ │ ├── resources │ │ ├── log4j.properties │ │ ├── master-config.txt │ │ ├── META-INF │ │ │ └── commandhandlers │ │ └── master-beans.xml │ │ └── java │ │ └── cn │ │ └── yang │ │ └── master │ │ └── client │ │ ├── exception │ │ ├── ConnectionException.java │ │ ├── FireCommandHandlerException.java │ │ ├── MasterClientException.java │ │ └── MasterChannelHandlerException.java │ │ ├── constant │ │ ├── MessageConstants.java │ │ ├── ConfigConstants.java │ │ └── ExceptionMessageConstants.java │ │ ├── ui │ │ ├── IDisplayPuppet.java │ │ ├── IMasterDesktop.java │ │ ├── listener │ │ │ ├── KeyBoardListener.java │ │ │ └── MouseListener.java │ │ └── impl │ │ │ ├── AbstractDisplayPuppet.java │ │ │ └── PuppetScreen.java │ │ ├── commandhandler │ │ ├── ConnectCommandHandler.java │ │ ├── AbstractMasterFireCommandHandler.java │ │ ├── ScreenCommandHandler.java │ │ ├── ControlFireCommandHandler.java │ │ ├── CommonFireCommandHandler.java │ │ └── AbstractMasterCommandHandler.java │ │ ├── MasterStarter.java │ │ └── netty │ │ ├── MasterNettyClientHandler.java │ │ └── MasterNettyClient.java ├── dependency-reduced-pom.xml ├── pom.xml └── master.iml ├── server ├── src │ └── main │ │ ├── resources │ │ ├── log4j.properties │ │ ├── server-config.txt │ │ ├── META-INF │ │ │ └── commandhandlers │ │ └── server-beans.xml │ │ └── java │ │ └── cn │ │ └── yang │ │ └── server │ │ ├── constant │ │ ├── MessageConstants.java │ │ └── ConfigConstants.java │ │ ├── commandhandler │ │ ├── HeartBeatCommandHandler.java │ │ ├── CommonFireCommandHandler.java │ │ ├── TerminateCommandHandler.java │ │ ├── ScreenCommandHandler.java │ │ ├── AbstractServerCommandHandler.java │ │ ├── ControlCommandHandler.java │ │ └── ConnectionCommandHandler.java │ │ ├── netty │ │ ├── ChannelPair.java │ │ ├── NettyServerHandler.java │ │ └── NettyServer.java │ │ └── ServerStarter.java ├── dependency-reduced-pom.xml ├── pom.xml └── server.iml ├── common ├── src │ └── main │ │ └── java │ │ └── cn │ │ └── yang │ │ └── common │ │ ├── netty │ │ ├── INettyClient.java │ │ └── ChannelInitializer.java │ │ ├── exception │ │ ├── ConfigParseException.java │ │ ├── TaskExecutorException.java │ │ ├── ConnectionException.java │ │ ├── CommandHandlerException.java │ │ ├── ResponseHandleException.java │ │ ├── CommandHandlerLoaderException.java │ │ └── ServerException.java │ │ ├── generator │ │ ├── SequenceGenerate.java │ │ ├── PuppetNameGenerate.java │ │ └── impl │ │ │ ├── SimplePuppetNameGenerator.java │ │ │ └── SimpleSequenceGenerator.java │ │ ├── constant │ │ ├── Constants.java │ │ └── ExceptionMessageConstants.java │ │ ├── command │ │ ├── handler │ │ │ └── ICommandHandler.java │ │ └── Commands.java │ │ ├── dto │ │ ├── Request.java │ │ ├── Response.java │ │ └── Invocation.java │ │ ├── InputEvent │ │ ├── MouseButton.java │ │ ├── MasterKeyEvent.java │ │ └── MasterMouseEvent.java │ │ ├── util │ │ ├── BeanUtil.java │ │ ├── MacUtils.java │ │ ├── TaskExecutors.java │ │ ├── PropertiesUtil.java │ │ ├── SerializationUtil.java │ │ └── CommandHandlerLoader.java │ │ └── serialization │ │ ├── ProtobufEncoder.java │ │ └── ProtobufDecoder.java ├── pom.xml └── common.iml ├── desktop-control-parent.iml └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | common/target 3 | master/target 4 | puppet/target 5 | server/target -------------------------------------------------------------------------------- /puppet/src/main/java/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Main-Class: cn.yang.puppet.client.PuppetStarter 3 | 4 | -------------------------------------------------------------------------------- /.idea/markdown-navigator/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /master/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cool-Coding/remote-desktop-control/HEAD/master/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /puppet/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cool-Coding/remote-desktop-control/HEAD/puppet/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /server/src/main/resources/log4j.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Cool-Coding/remote-desktop-control/HEAD/server/src/main/resources/log4j.properties -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /master/src/main/resources/master-config.txt: -------------------------------------------------------------------------------- 1 | #鼠标双击检测延迟间隔时间 2 | mouse.double.check.delay=2000 3 | 4 | #服务器IP地址 5 | #server.ip=144.34.157.127 6 | server.ip=192.168.254.1 7 | 8 | #服务器端口号 9 | server.port=1234 10 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/netty/INettyClient.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.netty; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/8/3 6 | */ 7 | public interface INettyClient { 8 | /** 9 | * 连接服务器 10 | */ 11 | void connect() throws Exception; 12 | } 13 | -------------------------------------------------------------------------------- /server/src/main/resources/server-config.txt: -------------------------------------------------------------------------------- 1 | #控制端控制傀儡端重试时间间隔(ms) 2 | master.connect.puppetName.retry.interval=1000 3 | 4 | #控制端控制傀儡端重试次数 5 | master.connect.puppetName.retry.times=10 6 | 7 | #服务器IP 8 | #server.ip=144.34.157.127 9 | server.ip=192.168.254.1 10 | 11 | #服务器端口 12 | server.port=1234 -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/ConfigParseException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | public class ConfigParseException extends RuntimeException { 4 | 5 | public ConfigParseException(String message, Throwable cause) { 6 | super(message,cause); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/generator/SequenceGenerate.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.generator; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/25 6 | */ 7 | public interface SequenceGenerate { 8 | 9 | /** 10 | * 生成下一个序号 11 | * @return 12 | */ 13 | int next(); 14 | } 15 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/constant/PuppetDynamicSetting.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.constant; 2 | 3 | import cn.yang.common.constant.Constants; 4 | 5 | public class PuppetDynamicSetting { 6 | /** 7 | * 清晰度[10,100] 8 | */ 9 | public static int quality = Constants.SCREEN_QUALITY; 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/generator/PuppetNameGenerate.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.generator; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | 5 | /** 6 | * @author Cool-Coding 7 | * 2018/8/3 8 | */ 9 | public interface PuppetNameGenerate { 10 | String getPuppetName(ChannelHandlerContext ctx); 11 | } 12 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/constant/Constants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.constant; 2 | 3 | /** 4 | * @author cool-coding 5 | * @date 2018/7/24 6 | */ 7 | public class Constants { 8 | public static final char MASTER='M'; 9 | public static final char PUPPET='P'; 10 | public static final int SCREEN_QUALITY=50; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/TaskExecutorException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * @date 2018/7/25 6 | */ 7 | public class TaskExecutorException extends Exception { 8 | public TaskExecutorException(String msg, Exception e) { 9 | super(msg, e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/exception/HeartBeatException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/25 6 | */ 7 | public class HeartBeatException extends Exception { 8 | public HeartBeatException(String msg,Exception e){ 9 | super(msg,e); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/constant/MessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/8/2 6 | */ 7 | public class MessageConstants { 8 | public static final String CONNECTION_SUCCEED="connect successfully"; 9 | public static final String RECEIVE_A_HEARTBEAT="receive a heart beat from puppet"; 10 | } 11 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/ConnectionException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class ConnectionException extends Exception { 8 | public ConnectionException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public ConnectionException(String msg, Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/exception/NullValueException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class NullValueException extends Exception { 8 | public NullValueException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public NullValueException(String msg, Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/CommandHandlerException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class CommandHandlerException extends Exception { 8 | public CommandHandlerException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public CommandHandlerException(String msg,Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/ResponseHandleException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class ResponseHandleException extends Exception { 8 | public ResponseHandleException(String msg, Exception e){ 9 | super(msg,e); 10 | } 11 | 12 | public ResponseHandleException(String msg){ 13 | super(msg); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/exception/ConnectionException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class ConnectionException extends Exception { 8 | public ConnectionException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public ConnectionException(String msg, Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/exception/FireCommandHandlerException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.exception; 2 | 3 | /** 4 | * @author cool-coding 5 | * @date 2018/7/26 6 | */ 7 | public class FireCommandHandlerException extends Exception{ 8 | public FireCommandHandlerException(String msg){ 9 | super(msg); 10 | } 11 | public FireCommandHandlerException(Exception e){ 12 | super(e); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/exception/MasterClientException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class MasterClientException extends Exception { 8 | public MasterClientException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public MasterClientException(String msg, Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/exception/PuppetClientException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class PuppetClientException extends Exception { 8 | public PuppetClientException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public PuppetClientException(String msg, Exception obj){ 13 | super(msg,obj); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/resources/META-INF/commandhandlers: -------------------------------------------------------------------------------- 1 | CONNECT=cn.yang.puppet.client.commandhandler.ConnectCommandHandler 2 | CONTROL=cn.yang.puppet.client.commandhandler.ControlCommandHandler 3 | KEYBOARD=cn.yang.puppet.client.commandhandler.KeyBoardEventCommandHandler 4 | MOUSE=cn.yang.puppet.client.commandhandler.MouseEventCommandHandler 5 | TERMINATE=cn.yang.puppet.client.commandhandler.TerminateCommandHandler 6 | QUALITY=cn.yang.puppet.client.commandhandler.QualityCommandHandler -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/CommandHandlerLoaderException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/27 6 | */ 7 | public class CommandHandlerLoaderException extends Exception { 8 | public CommandHandlerLoaderException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public CommandHandlerLoaderException(String msg, Exception e){ 13 | super(msg,e); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/constant/MessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/8/2 6 | */ 7 | public class MessageConstants { 8 | public static final String RECEIVE_SCREEN_SNAPSHOT="receive a snapshot from puppet"; 9 | public static final String CONNECT_SUCCESSFULLY="connect successfully"; 10 | public static final String PREPARING_TO_FIRE="is preparing to fire"; 11 | } 12 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/exception/MasterChannelHandlerException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.exception; 2 | 3 | /** 4 | * @author cool-coding 5 | * @date 2018/7/26 6 | */ 7 | public class MasterChannelHandlerException extends RuntimeException{ 8 | public MasterChannelHandlerException(String msg){ 9 | super(msg); 10 | } 11 | 12 | public MasterChannelHandlerException(String msg,Exception e){ 13 | super(msg,e); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/constant/MessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.constant; 2 | 3 | /** 4 | * @author cool-coding 5 | * @date 2018/7/24 6 | */ 7 | public class MessageConstants { 8 | public static final String SEND_A_HEARTBEAT="send a heartbeat to"; 9 | public static final String SEND_A_SCREENSNAPSHOT="send a snapshot to"; 10 | public static final String PUPPET_NAME_FROM_SERVER="Please remember that the puppet's name is"; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/IDisplayPuppet.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/8/2 6 | * 傀儡控制屏幕接口 7 | */ 8 | public interface IDisplayPuppet { 9 | /** 10 | * 启动窗口显示傀儡桌面 11 | */ 12 | void launch(); 13 | 14 | /** 15 | * 刷新桌面 16 | * @param bytes 17 | */ 18 | void refresh(byte[] bytes); 19 | 20 | /** 21 | * 22 | * @return 傀儡名称 23 | */ 24 | String getPuppetName(); 25 | } 26 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/command/handler/ICommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.command.handler; 2 | 3 | import io.netty.channel.ChannelHandlerContext; 4 | 5 | /** 6 | * @author Cool-Coding 7 | * 2018/7/27 8 | */ 9 | public interface ICommandHandler { 10 | /** 11 | * 12 | * @param ctx 当前channel处理器上下文 13 | * @param inbound channel输入对象 14 | * @throws Exception 异常 15 | */ 16 | void handle(ChannelHandlerContext ctx,T inbound) throws Exception; 17 | } 18 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/dto/Request.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.dto; 2 | 3 | /** 4 | * @author Cool-Coding 2018/7/24 5 | * 请求封装类 6 | */ 7 | public class Request extends Invocation { 8 | @Override 9 | public String toString() { 10 | return "Request{" + 11 | "id='" + getId() + '\'' + 12 | ", puppetName='" + getPuppetName() + '\'' + 13 | ", command=" + getCommand() + 14 | ", value=" + getValue() + 15 | '}'; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /master/src/main/resources/META-INF/commandhandlers: -------------------------------------------------------------------------------- 1 | CONNECT=cn.yang.master.client.commandhandler.ConnectCommandHandler 2 | CONTROL=cn.yang.master.client.commandhandler.ControlFireCommandHandler 3 | SCREEN=cn.yang.master.client.commandhandler.ScreenCommandHandler 4 | KEYBOARD=cn.yang.master.client.commandhandler.CommonFireCommandHandler 5 | MOUSE=cn.yang.master.client.commandhandler.CommonFireCommandHandler 6 | TERMINATE=cn.yang.master.client.commandhandler.CommonFireCommandHandler 7 | QUALITY=cn.yang.master.client.commandhandler.CommonFireCommandHandler -------------------------------------------------------------------------------- /.idea/libraries/Maven__log4j_log4j_1_2_17.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/ControlCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.dto.Response; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * @author Cool-Coding 8 | * 2018/7/27 9 | */ 10 | public class ControlCommandHandler extends AbstractPuppetCommandHandler { 11 | @Override 12 | protected void handle0(ChannelHandlerContext ctx, Response request) throws Exception { 13 | startUnderControlled(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/TerminateCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.dto.Response; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * @author Cool-Coding 8 | * 2018/7/27 9 | */ 10 | public class TerminateCommandHandler extends AbstractPuppetCommandHandler { 11 | @Override 12 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 13 | stopUnderControlled(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/ConnectCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.dto.Response; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * @author Cool-Coding 8 | * 2018/7/27 9 | */ 10 | public class ConnectCommandHandler extends AbstractMasterCommandHandler { 11 | @Override 12 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 13 | setChannelHandlerConext(ctx); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/src/main/resources/META-INF/commandhandlers: -------------------------------------------------------------------------------- 1 | CONNECT=cn.yang.server.commandhandler.ConnectionCommandHandler 2 | CONTROL=cn.yang.server.commandhandler.ControlCommandHandler 3 | HEARTBEAT=cn.yang.server.commandhandler.HeartBeatCommandHandler 4 | SCREEN=cn.yang.server.commandhandler.ScreenCommandHandler 5 | KEYBOARD=cn.yang.server.commandhandler.CommonFireCommandHandler 6 | MOUSE=cn.yang.server.commandhandler.CommonFireCommandHandler 7 | QUALITY=cn.yang.server.commandhandler.CommonFireCommandHandler 8 | TERMINATE=cn.yang.server.commandhandler.TerminateCommandHandler -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/exception/ServerException.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.exception; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author Cool-Coding 7 | * 2018/7/27 8 | */ 9 | public class ServerException extends Exception implements Serializable { 10 | 11 | private static final long serialVersionUID = -7515877337663696708L; 12 | 13 | public ServerException(String msg, Exception e){ 14 | super(msg,e); 15 | } 16 | 17 | public ServerException(String msg){ 18 | super(msg); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/generator/impl/SimplePuppetNameGenerator.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.generator.impl; 2 | 3 | import cn.yang.common.generator.PuppetNameGenerate; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | import java.util.UUID; 7 | 8 | /** 9 | * @author Cool-Coding 10 | * 2018/8/3 11 | */ 12 | public class SimplePuppetNameGenerator implements PuppetNameGenerate { 13 | @Override 14 | public String getPuppetName(ChannelHandlerContext ctx) { 15 | return UUID.randomUUID().toString(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /puppet/src/main/resources/puppet-config.txt: -------------------------------------------------------------------------------- 1 | #心跳间隔时间(ms) 2 | heartbeat.interval=2000 3 | 4 | #重新连接服务器间隔时间(ms) 5 | reconnect.interval=2000 6 | 7 | #向控制端发送屏幕截图间隔时间(ms) 8 | screen.refresh.frequency=200 9 | 10 | #心跳或发送屏幕截图任务检查时间间隔(ms) 11 | task.check.interval=200 12 | 13 | #服务器IP地址 14 | #server.ip=144.34.157.127 15 | server.ip=192.168.254.1 16 | 17 | #服务器端口号 18 | server.port=1234 19 | 20 | #接收到服务器错误消息超过这个阀值就断开连接 21 | error.count=5 22 | 23 | #robot的实例方式;可选javaRobot、goRobot 24 | robot=goRobot 25 | 26 | #goRobot的IP:PORT,只有当robot实现方式是goRobot时才有意义 27 | go.robot.endpoint=127.0.0.1:12345 -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_objenesis_objenesis_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/generator/impl/SimpleSequenceGenerator.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.generator.impl; 2 | 3 | import cn.yang.common.generator.SequenceGenerate; 4 | 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | /** 8 | * @author Cool-Coding 9 | * 2018/7/25 10 | */ 11 | public class SimpleSequenceGenerator implements SequenceGenerate { 12 | /** 13 | * 简单的序号生成器 14 | */ 15 | private final AtomicInteger sequence=new AtomicInteger(); 16 | 17 | @Override 18 | public int next() { 19 | return sequence.getAndIncrement(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_slf4j_slf4j_log4j12_1_7_25.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/constant/ConfigConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * @date 2018/7/25 6 | */ 7 | public class ConfigConstants { 8 | 9 | public static final String CONFIG_FILE_PATH= "server-config.txt"; 10 | public static final String MASTER_CONNECT_PUPPET_RETRY_INTERVAL="master.connect.puppet.retry.interval"; 11 | public static final String MASTER_CONNECT_PUPPET_RETRY_TIMES="master.connect.puppet.retry.times"; 12 | public static final String SERVER_IP="server.ip"; 13 | public static final String SERVER_PORT="server.port"; 14 | } 15 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__net_coobird_thumbnailator_0_4_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/MasterStarter.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client; 2 | 3 | import cn.yang.master.client.ui.IMasterDesktop; 4 | import org.springframework.context.support.ClassPathXmlApplicationContext; 5 | 6 | 7 | /** 8 | * @author Cool-Coding 9 | * 2018/7/25 10 | *Master启动器 11 | */ 12 | public class MasterStarter { 13 | 14 | public static void main(String[] args){ 15 | final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("master-beans.xml"); 16 | context.start(); 17 | context.getBean(IMasterDesktop.class).lanuch(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/ui/impl/PuppetDesktop.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.ui.impl; 2 | 3 | 4 | import cn.yang.common.netty.INettyClient; 5 | import cn.yang.puppet.client.netty.PuppetNettyClient; 6 | 7 | /** 8 | * @author Cool-Coding 9 | * 2018/7/25 10 | */ 11 | public class PuppetDesktop implements INettyClient { 12 | 13 | private INettyClient puppetClient; 14 | 15 | 16 | @Override 17 | public void connect() throws Exception{ 18 | puppetClient.connect(); 19 | } 20 | 21 | public void setPuppetClient(PuppetNettyClient puppetClient) { 22 | this.puppetClient = puppetClient; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/HeartBeatCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.dto.Request; 4 | import cn.yang.server.constant.MessageConstants; 5 | import io.netty.channel.ChannelHandlerContext; 6 | 7 | /** 8 | * @author Cool-Coding 9 | * 2018/7/27 10 | */ 11 | public class HeartBeatCommandHandler extends AbstractServerCommandHandler { 12 | @Override 13 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 14 | final String puppetName = request.getPuppetName(); 15 | debug(request, MessageConstants.RECEIVE_A_HEARTBEAT,puppetName); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_api_1_0_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/constant/ConfigConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/25 6 | */ 7 | public class ConfigConstants { 8 | /** 9 | * 配置文件名 10 | */ 11 | public static final String CONFIG_FILE_PATH= "master-config.txt"; 12 | 13 | /** 14 | * 判断鼠标是否双击延迟的时间 15 | */ 16 | public static final String MOUSE_DOUBLE_CHECK_DELAY="mouse.double.check.delay"; 17 | 18 | /** 19 | * 连接的服务器IP 20 | */ 21 | public static final String SERVER_IP="server.ip"; 22 | 23 | /** 24 | * 连接的服务器端口 25 | */ 26 | public static final String SERVER_PORT="server.port"; 27 | } 28 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_core_1_0_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/InputEvent/MouseButton.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.InputEvent; 2 | 3 | 4 | /** 5 | * @author cool-coding 6 | * @date 2018/7/28 7 | */ 8 | public enum MouseButton { 9 | /** 10 | * 鼠标左键 11 | */ 12 | LEFT, 13 | 14 | /** 15 | * 鼠标右键 16 | */ 17 | RIGHT, 18 | 19 | /** 20 | * 鼠标滚轮 21 | */ 22 | MIDDLE, 23 | 24 | /** 25 | * 拖动 26 | */ 27 | DRAGGED, 28 | 29 | /** 30 | * 滚轮 31 | */ 32 | WHEEL, 33 | 34 | /** 35 | * 单击 36 | */ 37 | CLICK, 38 | 39 | /** 40 | * 按下 41 | */ 42 | PRESSED, 43 | 44 | /** 45 | * 释放 46 | */ 47 | RELEASED 48 | } 49 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_runtime_1_0_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_context_5_0_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/Maven__com_dyuproject_protostuff_protostuff_collectionschema_1_0_8.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/command/Commands.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.command; 2 | 3 | /** 4 | * @author cool-coding 5 | * 2018/7/27 6 | * 命令 7 | */ 8 | public enum Commands{ 9 | /** 10 | * 控制端或傀儡端连接服务器时的命令 11 | */ 12 | CONNECT, 13 | 14 | /** 15 | * 控制命令 16 | * 1.主人向服务器发送控制请求 17 | * 2.服务器将控制命令发给傀儡 18 | * 3.傀儡收到控制命令,将向服务器发送截屏 19 | */ 20 | CONTROL, 21 | 22 | /** 23 | * 傀儡发送心跳给服务器 24 | */ 25 | HEARTBEAT, 26 | 27 | /** 28 | * 傀儡发送屏幕截图命令 29 | */ 30 | SCREEN, 31 | 32 | /** 33 | * 控制端发送键盘事件 34 | */ 35 | KEYBOARD, 36 | 37 | /** 38 | * 控制端发送鼠标事件 39 | */ 40 | MOUSE, 41 | 42 | /** 43 | * 断开控制傀儡 44 | */ 45 | TERMINATE, 46 | 47 | /** 48 | * 清晰度 49 | */ 50 | QUALITY 51 | } 52 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/netty/ChannelPair.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.netty; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | /** 6 | * @author Cool-Coding 7 | * @date 2018/7/24 8 | */ 9 | public class ChannelPair { 10 | /** 11 | * 控制端通道 12 | */ 13 | Channel masterChannel; 14 | 15 | /** 16 | * 傀儡端通道 17 | */ 18 | Channel puppetChannel; 19 | 20 | public Channel getMasterChannel() { 21 | return masterChannel; 22 | } 23 | 24 | public void setMasterChannel(Channel masterChannel) { 25 | this.masterChannel = masterChannel; 26 | } 27 | 28 | public Channel getPuppetChannel() { 29 | return puppetChannel; 30 | } 31 | 32 | public void setPuppetChannel(Channel puppetChannel) { 33 | this.puppetChannel = puppetChannel; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/dto/Response.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.dto; 2 | 3 | import cn.yang.common.exception.ServerException; 4 | 5 | /** 6 | * @author: cool coding 7 | * @date: 2018/1/7 8 | * 请求响应结果 9 | */ 10 | public class Response extends Invocation { 11 | /** 12 | * 异常 13 | */ 14 | private ServerException error; 15 | 16 | public ServerException getError() { 17 | return error; 18 | } 19 | 20 | public void setError(ServerException error) { 21 | this.error = error; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "Response{" + 27 | "id='" + getId() + '\'' + 28 | ", puppetName='" + getPuppetName() + '\'' + 29 | ", error=" + error + 30 | ", command=" + getCommand() + 31 | ", value=" + getValue() + 32 | '}'; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/ServerStarter.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server; 2 | 3 | import cn.yang.server.netty.NettyServer; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.context.support.ClassPathXmlApplicationContext; 7 | 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/25 12 | */ 13 | public class ServerStarter { 14 | /** logger */ 15 | private static final Logger LOGGER = LoggerFactory.getLogger(ServerStarter.class); 16 | 17 | public static void main(String[] args){ 18 | final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("server-beans.xml"); 19 | context.start(); 20 | /* 21 | * 将启动服务器放在spring初始化bean后,以便阻塞 22 | */ 23 | try { 24 | context.getBean(NettyServer.class).start(); 25 | }catch (Exception e){ 26 | LOGGER.error(e.getMessage()); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/constant/ExceptionMessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.constant; 2 | 3 | /** 4 | * @author cool-coding 5 | * @date 2018/7/26 6 | */ 7 | public class ExceptionMessageConstants { 8 | public static final String HANDLER_NOT_SUPPORTED = "Not supported channelHandler"; 9 | public static final String PUPPET_NAME_EMPTY = "The puppet's name is required"; 10 | public static final String MASTER_FIRE_COMMAND_ERROR = "Master can't handle this command"; 11 | public static final String FIRE_COMMAND_HANDLE_ERROR = "Command is not a subclass of AbstractMasterFireCommandHandler"; 12 | public static final String CONTRL_COMMAND_RESULT_ERROR= "The result of control command is not right.it should be a puppet name"; 13 | public static final String CONNECTION_SERVER_FAILED = "Connecting to server is failed"; 14 | public static final String LAUNCH_FAILED = "Startup error"; 15 | } 16 | -------------------------------------------------------------------------------- /puppet/src/main/proto/go_robot.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package service; 3 | 4 | message Point { 5 | uint32 x = 1; 6 | uint32 y = 2; 7 | } 8 | 9 | message MouseButton { 10 | uint32 x = 1; 11 | } 12 | 13 | message MouseScrolledRequest { 14 | uint32 distance = 2; 15 | } 16 | 17 | message Empty { 18 | 19 | } 20 | 21 | message CaptureScreenRequest { 22 | uint32 width = 1; 23 | uint32 height = 2; 24 | } 25 | 26 | message CaptureScreenResponse { 27 | bytes bitmap = 1; 28 | } 29 | 30 | service GoRobot { 31 | rpc MouseMove(Point) returns(Empty) {}; 32 | rpc MouseClick(MouseButton) returns(Empty) {}; 33 | rpc MouseDoubleClick(MouseButton) returns(Empty) {}; 34 | rpc MousePressed(MouseButton) returns(Empty) {}; 35 | rpc MouseReleased(MouseButton) returns(Empty) {}; 36 | rpc MouseScrolledUp(MouseScrolledRequest) returns(Empty) {}; 37 | rpc MouseScrolledDown(MouseScrolledRequest) returns(Empty) {}; 38 | rpc CaptureScreen(CaptureScreenRequest) returns(stream CaptureScreenResponse) {}; 39 | } -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/ui/MessageDialog.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.ui; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | 6 | /** 7 | * @author Cool-Coding 8 | * 2018/8/3 9 | */ 10 | public class MessageDialog extends JDialog { 11 | private JTextField message; 12 | 13 | public MessageDialog(String title) { 14 | setSize(400, 200); 15 | final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 16 | setLocation(screenSize.width / 2, screenSize.height / 2); 17 | message = new JTextField(); 18 | message.setFont(new Font("宋体", Font.PLAIN, 20)); 19 | setTitle(title); 20 | add(message); 21 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 22 | } 23 | 24 | public void showMessage(String message) { 25 | SwingUtilities.invokeLater(() -> { 26 | this.message.setText(message); 27 | MessageDialog.this.setVisible(true); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/constant/ConfigConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/25 6 | */ 7 | public class ConfigConstants { 8 | /** 9 | * 配置文件名 10 | */ 11 | public static final String CONFIG_FILE_PATH= "puppet-config.txt"; 12 | 13 | public static final String HEARTBEAT_INTERVAL="heartbeat.interval"; 14 | public static final String RECONNECT_INTERVAL="reconnect.interval"; 15 | public static final String TASK_CHECK_INTERVAL="task.check.interval"; 16 | public static final String SERVER_IP="server.ip"; 17 | public static final String SERVER_PORT="server.port"; 18 | public static final String ERROR_COUNT="error.count"; 19 | public static final String SCREEN_REFRESH_FREQUENCY="screen.refresh.frequency"; 20 | 21 | /** 22 | * goRobot 使用robotgo实现 23 | * javaRobot 使用java自带robot实现 24 | */ 25 | public static final String ROBOT = "robot"; 26 | public static final String GO_ROBOT_ENDPOINT = "go.robot.endpoint"; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/AbstractMasterFireCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.master.client.constant.ExceptionMessageConstants; 6 | import cn.yang.master.client.exception.FireCommandHandlerException; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/27 12 | */ 13 | public abstract class AbstractMasterFireCommandHandler extends AbstractMasterCommandHandler { 14 | 15 | @Override 16 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 17 | throw new UnsupportedOperationException(String.format("%s %s", ExceptionMessageConstants.MASTER_FIRE_COMMAND_ERROR,response.getCommand())); 18 | } 19 | 20 | /** 21 | * 控制端向服务器发送命令 22 | * @param puppetName 傀儡名称 23 | * @param command 命令 24 | * @param data 数据 25 | * 26 | */ 27 | public abstract void fire(String puppetName, Enum command, T data) throws FireCommandHandlerException; 28 | } 29 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/BeanUtil.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.beans.factory.BeanFactory; 5 | import org.springframework.beans.factory.BeanFactoryAware; 6 | 7 | /** 8 | * @author cool-coding 9 | * 2018/7/27 10 | * 获取bean的工作类 11 | */ 12 | public class BeanUtil implements BeanFactoryAware { 13 | private static BeanFactory beanFactory; 14 | 15 | @Override 16 | public void setBeanFactory(BeanFactory beanFactory) throws BeansException { 17 | BeanUtil.beanFactory=beanFactory; 18 | 19 | } 20 | 21 | @SuppressWarnings("unchecked") 22 | public static T getBean(Class aclass,String beanName){ 23 | return (T)beanFactory.getBean(aclass,beanName); 24 | } 25 | 26 | public static T getBean(Class aclass){ 27 | return (T)beanFactory.getBean(aclass); 28 | } 29 | 30 | public static T getBean(String name){ 31 | return (T)beanFactory.getBean(name); 32 | } 33 | 34 | public static T getBean(Class aclass,Object... args){ 35 | return (T)beanFactory.getBean(aclass,args); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/constant/ExceptionMessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/26 6 | */ 7 | public class ExceptionMessageConstants { 8 | public static final String PUPPET_HANDLER_ERROR = "the puppet netty client handler must be PuppetNettyClientHandler"; 9 | public static final String KEYBOARD_EVENT_ERROR = "The type of keyevent from server is wrong"; 10 | public static final String KEYBOARD_EVENT_NULL = "The value of keyevent from server is null"; 11 | public static final String MOUSE_EVENT_ERROR = "The type of mouseevent from server is wrong"; 12 | public static final String MOUSE_EVENT_NULL = "The value of mouseevent from server is null"; 13 | public static final String QUALITY_EVENT_VALUE_ERROR = "The value of quality from server is not the type Integer"; 14 | public static final String QUALITY_EVENT_VALUE_NULL = "The value of quality from server is null"; 15 | public static final String DISCONNECT_TO_SERVER = "Disconnect to server {}:{},will be reconnect to server in {} milliseconds"; 16 | public static final String CAPTURE_SCREEN_ERROR = "capturing the screen is error"; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/QualityCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.dto.Response; 4 | import cn.yang.puppet.client.constant.ExceptionMessageConstants; 5 | import cn.yang.puppet.client.constant.PuppetDynamicSetting; 6 | import cn.yang.puppet.client.exception.NullValueException; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/27 12 | */ 13 | public class QualityCommandHandler extends AbstractPuppetCommandHandler { 14 | 15 | 16 | @Override 17 | protected void handle0(ChannelHandlerContext ctx, Response request) throws Exception { 18 | final Object result = request.getValue(); 19 | if (result==null){ 20 | error(request,ExceptionMessageConstants.QUALITY_EVENT_VALUE_NULL); 21 | throw new NullValueException(ExceptionMessageConstants.QUALITY_EVENT_VALUE_NULL); 22 | } 23 | 24 | if (!(request.getValue() instanceof Integer)){ 25 | error(request,ExceptionMessageConstants.QUALITY_EVENT_VALUE_ERROR); 26 | throw new ClassCastException(ExceptionMessageConstants.QUALITY_EVENT_VALUE_ERROR); 27 | } 28 | 29 | PuppetDynamicSetting.quality = ((Integer)result); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/netty/NettyServerHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.netty; 2 | 3 | import cn.yang.common.command.handler.ICommandHandler; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.util.CommandHandlerLoader; 6 | import io.netty.channel.ChannelHandler; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.channel.SimpleChannelInboundHandler; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * @author Cool-Coding 2018/7/24 14 | */ 15 | @ChannelHandler.Sharable 16 | public class NettyServerHandler extends SimpleChannelInboundHandler { 17 | 18 | /** logger */ 19 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyServerHandler.class); 20 | 21 | @SuppressWarnings("unchecked") 22 | @Override 23 | protected void channelRead0(ChannelHandlerContext ctx, Request msg) throws Exception { 24 | LOGGER.debug(msg.toString()); 25 | final ICommandHandler commandHandler = CommandHandlerLoader.getCommandHandler(msg.getCommand()); 26 | commandHandler.handle(ctx,msg); 27 | } 28 | 29 | @Override 30 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 31 | LOGGER.info(cause.getMessage()); 32 | ctx.close(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/IMasterDesktop.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.netty.INettyClient; 5 | import cn.yang.master.client.exception.MasterClientException; 6 | 7 | /** 8 | * @author cool-coding 9 | * @date 2018/8/2 10 | */ 11 | public interface IMasterDesktop extends INettyClient{ 12 | /** 13 | * 桌面窗体设置 14 | */ 15 | void setting(); 16 | 17 | /** 18 | * 菜单 19 | */ 20 | void initMenu(); 21 | 22 | /** 23 | * 窗体 24 | */ 25 | void initBody(); 26 | 27 | /** 28 | * 启动 29 | */ 30 | void lanuch(); 31 | 32 | /** 33 | * 启动远程傀儡的屏幕 34 | * @param puppetName 35 | */ 36 | void lanuch(String puppetName); 37 | 38 | /** 39 | * 刷新远程屏幕 40 | * @param puppetName 傀儡名 41 | * @param bytes 截屏字节数组或截屏部分字节 42 | */ 43 | void refreshScreen(String puppetName,byte[] bytes); 44 | 45 | /** 46 | * 终止远程 47 | * @param puppetName 傀儡名 48 | */ 49 | void terminate(String puppetName); 50 | 51 | /** 52 | * 发送命令 53 | * @param puppetName 傀儡名 54 | * @param command 命令 55 | * @param data 数据 56 | */ 57 | void fireCommand(String puppetName,Enum command,Object data) throws MasterClientException; 58 | } 59 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/MacUtils.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.NetworkInterface; 7 | import java.net.SocketException; 8 | import java.util.Enumeration; 9 | 10 | /** 11 | * @author Cool-Coding 12 | * 2018/7/25 13 | */ 14 | public class MacUtils { 15 | 16 | /** logger */ 17 | private static final Logger LOGGER = LoggerFactory.getLogger(MacUtils.class); 18 | 19 | public static String getMAC(){ 20 | Enumeration el; 21 | String mac_s = ""; 22 | try { 23 | el = NetworkInterface.getNetworkInterfaces(); 24 | while (el.hasMoreElements()) { 25 | byte[] mac = el.nextElement().getHardwareAddress(); 26 | if (mac == null) { 27 | continue; 28 | } 29 | mac_s = hexByte(mac[0]) + "-" + hexByte(mac[1]) + "-" 30 | + hexByte(mac[2]) + "-" + hexByte(mac[3]) + "-" 31 | + hexByte(mac[4]) + "-" + hexByte(mac[5]); 32 | } 33 | } catch (SocketException e1) { 34 | LOGGER.error(e1.getMessage(),e1); 35 | } 36 | return mac_s; 37 | 38 | } 39 | 40 | private static String hexByte(byte b) { 41 | String s = "000000" + Integer.toHexString(b); 42 | return s.substring(s.length() - 2); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /server/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | desktop-control-parent 5 | cn.yang.remote 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | server 10 | 11 | server 12 | 13 | 14 | maven-compiler-plugin 15 | 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | maven-shade-plugin 22 | 3.2.1 23 | 24 | 25 | package 26 | 27 | shade 28 | 29 | 30 | 31 | 32 | cn.yang.server.ServerStarter 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/CommonFireCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.constant.ExceptionMessageConstants; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.dto.Response; 6 | import cn.yang.server.netty.ChannelPair; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelHandlerContext; 9 | 10 | import static cn.yang.common.constant.ExceptionMessageConstants.PUPPET_CONNECTION_LOST; 11 | 12 | /** 13 | * @author Cool-Coding 14 | * 2018/7/27 15 | */ 16 | public class CommonFireCommandHandler extends AbstractServerCommandHandler{ 17 | @Override 18 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 19 | final String puppetName = request.getPuppetName(); 20 | 21 | //检查傀儡端连接是否正常 22 | ChannelPair channelPair = CONNECTED_CHANNELPAIRS.get(puppetName); 23 | if (channelPair==null || channelPair.getPuppetChannel()==null || !channelPair.getPuppetChannel().isOpen()){ 24 | error(request, ExceptionMessageConstants.PUPPET_CONNECTION_LOST); 25 | sendError(request, ctx,PUPPET_CONNECTION_LOST); 26 | return; 27 | } 28 | 29 | //发送数据 30 | final Channel puppetChannel = channelPair.getPuppetChannel(); 31 | Response response; 32 | response = buildResponse(request,request.getCommand(),request.getValue()); 33 | puppetChannel.writeAndFlush(response); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /master/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | desktop-control-parent 5 | cn.yang.remote 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | master 10 | 11 | master 12 | 13 | 14 | maven-compiler-plugin 15 | 16 | 1.8 17 | 1.8 18 | 19 | 20 | 21 | maven-shade-plugin 22 | 3.2.1 23 | 24 | 25 | package 26 | 27 | shade 28 | 29 | 30 | 31 | 32 | cn.yang.master.client.MasterStarter 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/ScreenCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.dto.Response; 4 | import cn.yang.common.util.BeanUtil; 5 | import cn.yang.master.client.constant.MessageConstants; 6 | import cn.yang.master.client.ui.IMasterDesktop; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/27 12 | *傀儡传过来的屏幕命令处理逻辑 13 | */ 14 | public class ScreenCommandHandler extends AbstractMasterCommandHandler { 15 | 16 | @Override 17 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 18 | refreshScreen(response); 19 | } 20 | 21 | private void refreshScreen(Response response){ 22 | RefreshTask refreshTask = new RefreshTask(response); 23 | new Thread(refreshTask).start(); 24 | } 25 | 26 | class RefreshTask implements Runnable { 27 | Response response; 28 | 29 | RefreshTask(Response response) { 30 | this.response = response; 31 | } 32 | 33 | @Override 34 | public void run() { 35 | debug(response, MessageConstants.RECEIVE_SCREEN_SNAPSHOT); 36 | if(response.getValue() instanceof byte[]) { 37 | byte[] bytes=(byte[])response.getValue(); 38 | final IMasterDesktop desktop = BeanUtil.getBean(IMasterDesktop.class); 39 | desktop.refreshScreen(response.getPuppetName(),bytes); 40 | } 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/KeyBoardEventCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.InputEvent.MasterKeyEvent; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.puppet.client.constant.ExceptionMessageConstants; 6 | import cn.yang.puppet.client.exception.NullValueException; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/27 12 | */ 13 | public class KeyBoardEventCommandHandler extends AbstractPuppetCommandHandler { 14 | 15 | 16 | @Override 17 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 18 | Object obj=response.getValue(); 19 | 20 | if (obj==null){ 21 | error(response, ExceptionMessageConstants.KEYBOARD_EVENT_NULL); 22 | throw new NullValueException(ExceptionMessageConstants.KEYBOARD_EVENT_NULL); 23 | } 24 | 25 | if (!(obj instanceof MasterKeyEvent)){ 26 | error(response, ExceptionMessageConstants.KEYBOARD_EVENT_ERROR); 27 | throw new ClassCastException(ExceptionMessageConstants.KEYBOARD_EVENT_ERROR); 28 | } 29 | 30 | MasterKeyEvent keyEvent=(MasterKeyEvent)obj; 31 | debug(response,keyEvent.toString()); 32 | if(keyEvent.isPressed()){ 33 | REPLAY.keyPress(keyEvent); 34 | REPLAY.keyRelease(keyEvent); 35 | }else{ 36 | //为了处理pressed之后的延迟,press后立即释放 37 | //REPLAY.keyRelease(keyEvent); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/TerminateCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.constant.ExceptionMessageConstants; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.dto.Response; 6 | import cn.yang.server.netty.ChannelPair; 7 | import io.netty.channel.Channel; 8 | import io.netty.channel.ChannelHandlerContext; 9 | 10 | import static cn.yang.common.constant.ExceptionMessageConstants.PUPPET_CONNECTION_LOST; 11 | 12 | /** 13 | * @author Cool-Coding 14 | * 2018/7/27 15 | */ 16 | public class TerminateCommandHandler extends AbstractServerCommandHandler{ 17 | @Override 18 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 19 | final String puppetName = request.getPuppetName(); 20 | 21 | //检查傀儡端连接是否正常 22 | ChannelPair channelPair = CONNECTED_CHANNELPAIRS.get(puppetName); 23 | if (channelPair==null || channelPair.getPuppetChannel()==null || !channelPair.getPuppetChannel().isOpen()){ 24 | error(request, ExceptionMessageConstants.PUPPET_CONNECTION_LOST); 25 | sendError(request, ctx,PUPPET_CONNECTION_LOST); 26 | return; 27 | } 28 | 29 | // 删除链接 30 | channelPair.setMasterChannel(null); 31 | 32 | //发送数据 33 | final Channel puppetChannel = channelPair.getPuppetChannel(); 34 | Response response=null; 35 | response = buildResponse(request,request.getCommand(),request.getValue()); 36 | puppetChannel.writeAndFlush(response); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | desktop-control-parent 7 | cn.yang.remote 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | common 13 | 14 | 15 | 16 | 17 | 18 | com.dyuproject.protostuff 19 | protostuff-core 20 | 1.0.8 21 | 22 | 23 | 24 | com.dyuproject.protostuff 25 | protostuff-runtime 26 | 1.0.8 27 | 28 | 29 | 30 | 31 | org.objenesis 32 | objenesis 33 | 2.1 34 | 35 | 36 | 37 | 38 | net.coobird 39 | thumbnailator 40 | 0.4.8 41 | 42 | 43 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/ui/IReplay.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.ui; 2 | 3 | import cn.yang.common.InputEvent.MasterKeyEvent; 4 | import cn.yang.common.InputEvent.MasterMouseEvent; 5 | 6 | /** 7 | * @author Cool-Coding 8 | * 2018/8/2 9 | */ 10 | public interface IReplay { 11 | /** 12 | * 键按下 13 | * @param keyEvent 14 | */ 15 | void keyPress(MasterKeyEvent keyEvent); 16 | 17 | /** 18 | * 键释放 19 | * @param keyEvent 20 | */ 21 | void keyRelease(MasterKeyEvent keyEvent); 22 | 23 | /** 24 | * 鼠标单击 25 | * @param mouseEvent 26 | */ 27 | void mouseClick(MasterMouseEvent mouseEvent); 28 | 29 | /** 30 | * 滚轮滚动 31 | * @param mouseEvent 32 | */ 33 | void mouseWheel(MasterMouseEvent mouseEvent); 34 | 35 | /** 36 | * 鼠标键按下 37 | * @param mouseEvent 38 | */ 39 | void mousePress(MasterMouseEvent mouseEvent); 40 | 41 | /** 42 | * 鼠标键释放 43 | * @param mouseEvent 44 | */ 45 | void mouseRelease(MasterMouseEvent mouseEvent); 46 | 47 | /** 48 | * 鼠标移动 49 | * @param site 50 | */ 51 | void mouseMove(int[] site); 52 | 53 | /** 54 | * 鼠标双击 55 | * @param mouseEvent 56 | */ 57 | void mouseDoubleClick(MasterMouseEvent mouseEvent); 58 | 59 | /** 60 | * 鼠标拖动 61 | * @param mouseEvent 62 | * @param site 63 | */ 64 | void mouseDragged(MasterMouseEvent mouseEvent, int[] site); 65 | 66 | 67 | /** 68 | * 截屏 69 | * @return 图像字节数组 70 | */ 71 | byte[] getScreenSnapshot(); 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/serialization/ProtobufEncoder.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.serialization; 2 | 3 | import cn.yang.common.dto.Request; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.common.util.SerializationUtil; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.MessageToByteEncoder; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | /** 13 | * User:cool coding 14 | * Date:2018/1/7 15 | * Time:21:33 16 | * 编码 17 | */ 18 | public class ProtobufEncoder extends MessageToByteEncoder { 19 | private Class genericClass; 20 | 21 | public ProtobufEncoder(Class genericClass) { 22 | this.genericClass = genericClass; 23 | } 24 | private static final Logger LOGGER= LoggerFactory.getLogger(ProtobufEncoder.class); 25 | 26 | @Override 27 | protected void encode(ChannelHandlerContext ctx, Object in, ByteBuf out) throws Exception { 28 | if(genericClass.isInstance(in)){ 29 | byte[] data= SerializationUtil.serialize(in); 30 | int length=data.length; 31 | out.writeInt(length); 32 | out.writeBytes(data); 33 | if (genericClass== Request.class) { 34 | LOGGER.debug("send data to server,size:{}", length); 35 | }else if(genericClass== Response.class){ 36 | LOGGER.debug("send data to client,size:{}",length); 37 | } 38 | 39 | } 40 | } 41 | 42 | @Override 43 | public boolean isSharable() { 44 | return super.isSharable(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /server/src/main/resources/server-beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /desktop-control-parent.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/dto/Invocation.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.dto; 2 | 3 | import cn.yang.common.command.Commands; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author: cool coding 9 | * @date: 2018/1/7 10 | * 控制端/服务器/傀儡端 三者之间传输的对象 11 | */ 12 | public class Invocation implements Serializable { 13 | 14 | /** 15 | * ID(客户端标识(控制端为'M',傀儡端为'P')+MAC地址+序列号) 16 | */ 17 | private String id; 18 | 19 | /** 20 | * 傀儡名 21 | */ 22 | private String puppetName; 23 | 24 | 25 | /** 26 | * 命令 27 | */ 28 | private Enum command; 29 | 30 | /** 31 | * 值 32 | */ 33 | private Object value; 34 | 35 | public String getId() { 36 | return id; 37 | } 38 | 39 | public void setId(String id) { 40 | this.id = id; 41 | } 42 | 43 | public void setValue(Object value) { 44 | this.value = value; 45 | } 46 | 47 | public Enum getCommand() { 48 | return command; 49 | } 50 | 51 | public void setCommand(Enum command) { 52 | this.command = command; 53 | } 54 | 55 | public Object getValue() { 56 | return value; 57 | } 58 | 59 | public String getPuppetName() { 60 | return puppetName; 61 | } 62 | 63 | public void setPuppetName(String puppetName) { 64 | this.puppetName = puppetName; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "Invocation{" + 70 | "id='" + id + '\'' + 71 | ", puppetName='" + puppetName + '\'' + 72 | ", command=" + command + 73 | ", value=" + value + 74 | '}'; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/netty/ChannelInitializer.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.netty; 2 | 3 | import cn.yang.common.serialization.ProtobufDecoder; 4 | import cn.yang.common.serialization.ProtobufEncoder; 5 | import io.netty.channel.ChannelHandler; 6 | import io.netty.channel.socket.SocketChannel; 7 | 8 | /** 9 | * @author Cool-Coding 10 | * 2018/7/25 11 | * 使用单例,由Spring管理 12 | */ 13 | public class ChannelInitializer extends io.netty.channel.ChannelInitializer { 14 | 15 | /** 16 | * 处理器 17 | */ 18 | private ChannelHandler channelHandler; 19 | 20 | private Class requestClass; 21 | private Class responseClass; 22 | 23 | public ChannelInitializer(Class requestClass, Class responseClass){ 24 | this.requestClass=requestClass; 25 | this.responseClass=responseClass; 26 | } 27 | 28 | /** 29 | * 每新建一个连接,都添加一遍encode/decoder/channelHandler,会报错:不是@Sharable 30 | * 但是如果在每个类上添加@Shareable注解,由于encoder、decoder继承ByteToMessageDecoder, 31 | * 而ByteToMessageDecoder的子类不允许有@Sharable注解,所以encoder和decoder采用新建对象的方式 32 | * @param ch 33 | * @throws Exception 34 | */ 35 | @Override 36 | protected void initChannel(SocketChannel ch) throws Exception { 37 | ch.pipeline() 38 | .addLast(new ProtobufDecoder(requestClass)) 39 | .addLast(new ProtobufEncoder(responseClass)) 40 | .addLast(channelHandler); 41 | } 42 | 43 | public void setChannelHandler(ChannelHandler channelHandler) { 44 | this.channelHandler = channelHandler; 45 | } 46 | 47 | public ChannelHandler getChannelHandler() { 48 | return channelHandler; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/InputEvent/MasterKeyEvent.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.InputEvent; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * 2018/7/26 6 | */ 7 | public class MasterKeyEvent { 8 | 9 | /** 10 | * 按键状态 11 | * TRUE.按下 12 | * FALSE.释放 13 | */ 14 | private boolean pressed; 15 | 16 | private boolean altDown; 17 | private boolean ctrlDown; 18 | private boolean shiftDown; 19 | 20 | private int keyCode; 21 | 22 | public boolean isPressed() { 23 | return pressed; 24 | } 25 | 26 | public void setPressed(boolean pressed) { 27 | this.pressed = pressed; 28 | } 29 | 30 | public boolean isAltDown() { 31 | return altDown; 32 | } 33 | 34 | public void setAltDown(boolean altDown) { 35 | this.altDown = altDown; 36 | } 37 | 38 | public boolean isCtrlDown() { 39 | return ctrlDown; 40 | } 41 | 42 | public void setCtrlDown(boolean ctrlDown) { 43 | this.ctrlDown = ctrlDown; 44 | } 45 | 46 | public boolean isShiftDown() { 47 | return shiftDown; 48 | } 49 | 50 | public void setShiftDown(boolean shiftDown) { 51 | this.shiftDown = shiftDown; 52 | } 53 | 54 | public int getKeyCode() { 55 | return keyCode; 56 | } 57 | 58 | public void setKeyCode(int keyCode) { 59 | this.keyCode = keyCode; 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "MasterKeyEvent{" + 65 | "pressed=" + pressed + 66 | ", altDown=" + altDown + 67 | ", ctrlDown=" + ctrlDown + 68 | ", shiftDown=" + shiftDown + 69 | ", keyCode=" + keyCode + 70 | '}'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/constant/ExceptionMessageConstants.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.constant; 2 | 3 | /** 4 | * @author Cool-Coding 5 | * @date 2018/7/24 6 | */ 7 | public class ExceptionMessageConstants { 8 | public static final String WRONG_CLIENT_TYPE ="reqestId must start with 'M'(master) 或 'P'(puppet)"; 9 | public static final String REQUIRED_REQUESTID ="requestId is required"; 10 | public static final String CONNECTION_EXIST ="the same connection exists"; 11 | public static final String WRONG_CONNECT_VALUE ="wrong connection value"; 12 | public static final String MASTER_CONNECTION_LOST ="connection of master is lost"; 13 | public static final String PUPPET_CONNECTION_LOST ="connection of puppet is lost"; 14 | public static final String CONNECT_PUPPET_FAILED ="connect puppet failed"; 15 | public static final String REQUIRED_PUPPET_NAME ="the puppet name is required"; 16 | public static final String REQUIRED_COMMAND ="command is required"; 17 | public static final String COMMAND_HANDLER_ERROR ="CommandHandler must implements ICommandHandler and only have one interface"; 18 | public static final String COMMAND_HANDLER_NOT_FOUND ="CommandHandler is not found"; 19 | public static final String EXTENSION_NOT_FOUND ="sequence generator is not found"; 20 | public static final String COMMANDHANDLERS_FILE_NOT_FOUND ="the file of commandhandlers is not found"; 21 | public static final String COMMANDHANDLERS_FILE_CONFIG_ERROR="the file of commandhandlers is configured incorrect"; 22 | public static final String ILLEGAL_STATUS ="illegal status"; 23 | } 24 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/ScreenCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.server.netty.ChannelPair; 6 | import io.netty.channel.Channel; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | import static cn.yang.common.constant.ExceptionMessageConstants.MASTER_CONNECTION_LOST; 10 | import static cn.yang.common.constant.ExceptionMessageConstants.WRONG_CONNECT_VALUE; 11 | 12 | /** 13 | * @author Cool-Coding 14 | * 2018/7/27 15 | */ 16 | public class ScreenCommandHandler extends AbstractServerCommandHandler { 17 | @Override 18 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 19 | final String puppetName = request.getPuppetName(); 20 | 21 | //检查请求的内容 22 | if(!(request.getValue() instanceof byte[])){ 23 | final String should_be_bufferedImage = String.format("%s %s %s", WRONG_CONNECT_VALUE, request.getValue(), "should be BufferedImage"); 24 | error(request,should_be_bufferedImage); 25 | sendError(request,ctx,should_be_bufferedImage); 26 | return; 27 | } 28 | 29 | //检查控制端连接是否正常 30 | final ChannelPair channelPair = CONNECTED_CHANNELPAIRS.get(puppetName); 31 | if (channelPair==null || channelPair.getMasterChannel()==null || !channelPair.getMasterChannel().isOpen()){ 32 | error(request,MASTER_CONNECTION_LOST); 33 | sendError(request,ctx,MASTER_CONNECTION_LOST); 34 | return; 35 | } 36 | 37 | final Channel masterChannel = channelPair.getMasterChannel(); 38 | masterChannel.writeAndFlush(buildResponse(request, Commands.SCREEN,request.getValue())); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/ControlFireCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.dto.Response; 6 | import cn.yang.common.exception.CommandHandlerException; 7 | import cn.yang.common.util.BeanUtil; 8 | import cn.yang.master.client.constant.ExceptionMessageConstants; 9 | import cn.yang.master.client.exception.ConnectionException; 10 | import cn.yang.master.client.exception.FireCommandHandlerException; 11 | import cn.yang.master.client.ui.IMasterDesktop; 12 | import io.netty.channel.ChannelHandlerContext; 13 | 14 | /** 15 | * @author Cool-Coding 16 | * 2018/7/27 17 | */ 18 | public class ControlFireCommandHandler extends AbstractMasterFireCommandHandler { 19 | 20 | @Override 21 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 22 | if(response.getValue() instanceof String) { 23 | //控制成功后,创建一个PuppetScreen对象,准备显示Puppet屏幕 24 | final IMasterDesktop desktop = BeanUtil.getBean(IMasterDesktop.class); 25 | desktop.lanuch((String) response.getValue()); 26 | }else{ 27 | throw new CommandHandlerException(ExceptionMessageConstants.CONTRL_COMMAND_RESULT_ERROR); 28 | } 29 | } 30 | 31 | @Override 32 | public void fire(String puppetName,Enum command,String puppetName2) throws FireCommandHandlerException { 33 | if (puppetName==null){ 34 | throw new FireCommandHandlerException(ExceptionMessageConstants.PUPPET_NAME_EMPTY); 35 | } 36 | 37 | final Request request = buildRequest(Commands.CONTROL, puppetName, null); 38 | try { 39 | sendRequest(request); 40 | }catch (ConnectionException e){ 41 | throw new FireCommandHandlerException(e); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/serialization/ProtobufDecoder.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.serialization; 2 | 3 | import cn.yang.common.dto.Request; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.common.util.SerializationUtil; 6 | import io.netty.buffer.ByteBuf; 7 | import io.netty.channel.ChannelHandlerContext; 8 | import io.netty.handler.codec.ByteToMessageDecoder; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * User:cool coding 16 | * Date:2018/1/7 17 | * Time:21:27 18 | * 解码 19 | */ 20 | public class ProtobufDecoder extends ByteToMessageDecoder { 21 | private Class genericClass; 22 | 23 | public ProtobufDecoder(Class genericClass) { 24 | this.genericClass = genericClass; 25 | } 26 | private static final Logger LOGGER= LoggerFactory.getLogger(ProtobufDecoder.class); 27 | 28 | @Override 29 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception { 30 | if(in.readableBytes()<4){ 31 | return; 32 | } 33 | 34 | in.markReaderIndex(); 35 | 36 | int dataLength=in.readInt(); 37 | if(dataLength<0) ctx.close(); 38 | 39 | if(in.readableBytes() < dataLength){ 40 | in.resetReaderIndex(); 41 | return; 42 | } 43 | 44 | if (genericClass== Request.class) { 45 | LOGGER.debug("receive data from client,size:{}", dataLength); 46 | }else if(genericClass== Response.class){ 47 | LOGGER.debug("receive data from server,size:{}",dataLength); 48 | } 49 | 50 | byte[] data=new byte[dataLength]; 51 | in.readBytes(data); 52 | 53 | Object obj= SerializationUtil.deSerialize(data,genericClass); 54 | out.add(obj); 55 | 56 | } 57 | 58 | @Override 59 | public boolean isSharable() { 60 | return super.isSharable(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/CommonFireCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.util.TaskExecutors; 6 | import cn.yang.master.client.constant.ExceptionMessageConstants; 7 | import cn.yang.master.client.exception.ConnectionException; 8 | import cn.yang.master.client.exception.FireCommandHandlerException; 9 | 10 | import java.util.concurrent.ArrayBlockingQueue; 11 | 12 | /** 13 | * @author Cool-Coding 14 | * 2018/7/27 15 | */ 16 | public class CommonFireCommandHandler extends AbstractMasterFireCommandHandler { 17 | private ArrayBlockingQueue blockingQueue=new ArrayBlockingQueue<>(100); 18 | 19 | private Runnable task=new Runnable() { 20 | @Override 21 | public void run() { 22 | while (true) { 23 | Request request=null; 24 | try { 25 | while ((request = blockingQueue.take()) != null) { 26 | try { 27 | sendRequest(request); 28 | } catch (ConnectionException e) { 29 | error(request, e.getMessage()); 30 | } 31 | } 32 | }catch (InterruptedException e){ 33 | error(this,e.getMessage()); 34 | } 35 | } 36 | } 37 | }; 38 | 39 | public CommonFireCommandHandler(){ 40 | TaskExecutors.submit(task,1000); 41 | } 42 | 43 | @Override 44 | public void fire(String puppetName,Enum command,Object data) throws FireCommandHandlerException { 45 | if (puppetName==null){ 46 | throw new FireCommandHandlerException(ExceptionMessageConstants.PUPPET_NAME_EMPTY); 47 | } 48 | 49 | final Request request = buildRequest(command, puppetName, data); 50 | 51 | blockingQueue.offer(request); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/MouseEventCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.InputEvent.MasterMouseEvent; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.puppet.client.constant.ExceptionMessageConstants; 6 | import cn.yang.puppet.client.exception.NullValueException; 7 | import io.netty.channel.ChannelHandlerContext; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/27 12 | */ 13 | public class MouseEventCommandHandler extends AbstractPuppetCommandHandler { 14 | 15 | 16 | @Override 17 | protected void handle0(ChannelHandlerContext ctx, Response response) throws Exception { 18 | Object obj=response.getValue(); 19 | 20 | if (obj==null){ 21 | error(response, ExceptionMessageConstants.MOUSE_EVENT_NULL); 22 | throw new NullValueException(ExceptionMessageConstants.MOUSE_EVENT_NULL); 23 | } 24 | 25 | if (!(obj instanceof MasterMouseEvent)){ 26 | error(response, ExceptionMessageConstants.MOUSE_EVENT_ERROR); 27 | throw new ClassCastException(ExceptionMessageConstants.MOUSE_EVENT_ERROR); 28 | } 29 | 30 | MasterMouseEvent mouseEvent=(MasterMouseEvent)obj; 31 | if(mouseEvent.isClicked()){ 32 | REPLAY.mouseClick(mouseEvent); 33 | } 34 | 35 | if(mouseEvent.isDoubleClicked()){ 36 | REPLAY.mouseDoubleClick(mouseEvent); 37 | } 38 | 39 | if(mouseEvent.isDragged()){ 40 | REPLAY.mouseDragged(mouseEvent,mouseEvent.getSite()); 41 | } 42 | 43 | if(mouseEvent.isMouseMoved()){ 44 | REPLAY.mouseMove(mouseEvent.getSite()); 45 | } 46 | 47 | if(mouseEvent.isMouseWheel()){ 48 | REPLAY.mouseWheel(mouseEvent); 49 | } 50 | 51 | if(mouseEvent.isMousePressed()){ 52 | REPLAY.mousePress(mouseEvent); 53 | } 54 | 55 | if(mouseEvent.isMouseReleased()){ 56 | REPLAY.mouseRelease(mouseEvent); 57 | } 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /master/src/main/resources/master-beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/listener/KeyBoardListener.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui.listener; 2 | 3 | import cn.yang.common.InputEvent.MasterKeyEvent; 4 | import cn.yang.common.command.Commands; 5 | import cn.yang.common.util.BeanUtil; 6 | import cn.yang.master.client.exception.MasterClientException; 7 | import cn.yang.master.client.netty.MasterNettyClient; 8 | import cn.yang.master.client.ui.IDisplayPuppet; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | import javax.swing.*; 13 | import java.awt.event.KeyAdapter; 14 | import java.awt.event.KeyEvent; 15 | 16 | /** 17 | * @author Cool-Coding 18 | * 2018/7/26 19 | */ 20 | public class KeyBoardListener extends KeyAdapter { 21 | 22 | private IDisplayPuppet puppetScreen; 23 | private MasterNettyClient masterClient; 24 | private static final Logger LOGGER= LoggerFactory.getLogger(KeyBoardListener.class); 25 | 26 | public KeyBoardListener(IDisplayPuppet puppetScreen){ 27 | this.puppetScreen=puppetScreen; 28 | masterClient = BeanUtil.getBean(MasterNettyClient.class, "masterClient"); 29 | } 30 | 31 | @Override 32 | public void keyPressed(KeyEvent e) { 33 | LOGGER.debug(KeyEvent.getKeyText(e.getKeyCode())+" pressed"); 34 | fireCommand(e,true); 35 | } 36 | 37 | @Override 38 | public void keyReleased(KeyEvent e) { 39 | LOGGER.debug(KeyEvent.getKeyText(e.getKeyCode())+" released"); 40 | fireCommand(e,false); 41 | } 42 | 43 | private void fireCommand(KeyEvent e, boolean pressed){ 44 | final MasterKeyEvent keyEvent = new MasterKeyEvent(); 45 | keyEvent.setPressed(pressed); 46 | if (e.isAltDown()){ 47 | keyEvent.setAltDown(true); 48 | } 49 | 50 | if (e.isControlDown()){ 51 | keyEvent.setCtrlDown(true); 52 | } 53 | 54 | if (e.isShiftDown()){ 55 | keyEvent.setShiftDown(true); 56 | } 57 | keyEvent.setKeyCode(e.getKeyCode()); 58 | try { 59 | masterClient.fireCommand(puppetScreen.getPuppetName(), Commands.KEYBOARD, keyEvent); 60 | }catch (MasterClientException e2){ 61 | JOptionPane.showMessageDialog(null,e2.getMessage()); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/PuppetStarter.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client; 2 | 3 | import cn.yang.common.util.PropertiesUtil; 4 | import cn.yang.puppet.client.constant.ConfigConstants; 5 | import cn.yang.puppet.client.ui.impl.PuppetDesktop; 6 | import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; 7 | import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.context.support.ClassPathXmlApplicationContext; 11 | 12 | /** 13 | * @author Cool-Coding 14 | * 2018/7/25 15 | * Puppet启动器 16 | */ 17 | public class PuppetStarter { 18 | /** logger */ 19 | private static final Logger LOGGER = LoggerFactory.getLogger(PuppetStarter.class); 20 | 21 | public static io.grpc.Channel goRobotChannel; 22 | 23 | public static void main(String[] args){ 24 | // 1. 初始化spring类 25 | final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("puppet-beans.xml"); 26 | context.start(); 27 | 28 | // 2. 连接gorobot 29 | ConnectGoRobotIfNecessary(); 30 | 31 | /*3. 连接server 32 | * 将连接服务器放在spring初始化bean后,有两个原因: 33 | * 3.1.防止阻塞spring初始化bean 34 | * 3.2.连接服务器后可能需要做出一些处理,处理中可能需要使用spring bean,而此时spring bean可能尚未 35 | * 初始化完成。例如cn.yang.puppet.client.commandhandler.AbstractPuppetCommandHandler类中 36 | * 静态成员变量generator 37 | */ 38 | try { 39 | context.getBean(PuppetDesktop.class).connect(); 40 | }catch (Exception e){ 41 | LOGGER.error(e.getMessage(),e); 42 | } 43 | } 44 | 45 | private static void ConnectGoRobotIfNecessary() { 46 | String robot = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH,ConfigConstants.ROBOT); 47 | if ("goRobot".equals(robot)) { 48 | String endpoint = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.GO_ROBOT_ENDPOINT,"127.0.0.1:12345"); 49 | String[] endpointSplit = endpoint.split(":"); 50 | goRobotChannel = NettyChannelBuilder.forAddress(endpointSplit[0], Integer.parseInt(endpointSplit[1])) 51 | .negotiationType(NegotiationType.PLAINTEXT) 52 | .build(); 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /puppet/src/main/resources/puppet-beans.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/netty/NettyServer.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.netty; 2 | 3 | import cn.yang.common.util.PropertiesUtil; 4 | import cn.yang.server.constant.ConfigConstants; 5 | import io.netty.bootstrap.ServerBootstrap; 6 | import io.netty.channel.ChannelFuture; 7 | import io.netty.channel.ChannelHandler; 8 | import io.netty.channel.ChannelOption; 9 | import io.netty.channel.nio.NioEventLoopGroup; 10 | import io.netty.channel.socket.nio.NioServerSocketChannel; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | /** 15 | * @author Cool-Coding 2018/7/24 16 | */ 17 | public class NettyServer { 18 | /** 19 | * 处理器初始化器 20 | */ 21 | private ChannelHandler channelInitialize; 22 | 23 | 24 | /** logger */ 25 | private static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class); 26 | 27 | private void bind(String host,int port) throws InterruptedException{ 28 | final NioEventLoopGroup boss=new NioEventLoopGroup(); 29 | final NioEventLoopGroup worker=new NioEventLoopGroup(); 30 | 31 | try{ 32 | final ServerBootstrap bootstrap = new ServerBootstrap(); 33 | bootstrap.group(boss,worker) 34 | .channel(NioServerSocketChannel.class) 35 | .option(ChannelOption.SO_BACKLOG,1024) 36 | .childOption(ChannelOption.SO_KEEPALIVE,true) 37 | .childHandler(channelInitialize); 38 | 39 | final ChannelFuture f = bootstrap.bind(host, port).sync(); 40 | 41 | LOGGER.info("server start on port:{}",port); 42 | f.channel().closeFuture().sync(); 43 | }finally { 44 | boss.shutdownGracefully(); 45 | worker.shutdownGracefully(); 46 | } 47 | } 48 | 49 | 50 | public void start() throws Exception{ 51 | String ip = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_IP); 52 | int port = PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_PORT); 53 | try { 54 | bind(ip,port); 55 | }catch (InterruptedException e){ 56 | LOGGER.error(e.getMessage(),e); 57 | throw e; 58 | } 59 | } 60 | 61 | public void setChannelInitialize(ChannelHandler channelInitialize) { 62 | this.channelInitialize = channelInitialize; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /master/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | desktop-control-parent 7 | cn.yang.remote 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | master 13 | 14 | 15 | 16 | cn.yang.remote 17 | common 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | 24 | master 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 31 | 1.8 32 | 1.8 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-shade-plugin 40 | 3.2.1 41 | 42 | 43 | package 44 | 45 | shade 46 | 47 | 48 | 49 | 51 | cn.yang.master.client.MasterStarter 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | desktop-control-parent 7 | cn.yang.remote 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | server 13 | 14 | 15 | 16 | 17 | cn.yang.remote 18 | common 19 | 1.0-SNAPSHOT 20 | 21 | 22 | 23 | 24 | 25 | server 26 | 27 | 28 | 29 | org.apache.maven.plugins 30 | maven-compiler-plugin 31 | 32 | 1.8 33 | 1.8 34 | 35 | 36 | 37 | 38 | 39 | org.apache.maven.plugins 40 | maven-shade-plugin 41 | 3.2.1 42 | 43 | 44 | package 45 | 46 | shade 47 | 48 | 49 | 50 | 52 | cn.yang.server.ServerStarter 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cn.yang.remote 8 | desktop-control-parent 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | master 13 | server 14 | puppet 15 | common 16 | 17 | 18 | 19 | 20 | 21 | 22 | io.netty 23 | netty-all 24 | 4.1.42.Final 25 | 26 | 27 | 28 | 29 | org.springframework 30 | spring-core 31 | 5.3.19 32 | 33 | 34 | org.springframework 35 | spring-beans 36 | 5.3.18 37 | 38 | 39 | org.springframework 40 | spring-context 41 | 5.0.6.RELEASE 42 | 43 | 44 | 45 | 46 | org.slf4j 47 | slf4j-api 48 | 1.7.25 49 | 50 | 51 | org.slf4j 52 | slf4j-log4j12 53 | 1.7.25 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-compiler-plugin 62 | 63 | 1.8 64 | 1.8 65 | 1.8 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /puppet/dependency-reduced-pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | desktop-control-parent 5 | cn.yang.remote 6 | 1.0-SNAPSHOT 7 | 8 | 4.0.0 9 | puppet 10 | 11 | 12 | 13 | kr.motd.maven 14 | os-maven-plugin 15 | 1.5.0.Final 16 | 17 | 18 | puppet 19 | 20 | 21 | maven-compiler-plugin 22 | 3.8.1 23 | 24 | 1.8 25 | 1.8 26 | 27 | 28 | 29 | maven-shade-plugin 30 | 3.2.1 31 | 32 | 33 | package 34 | 35 | shade 36 | 37 | 38 | 39 | 40 | cn.yang.puppet.client.PuppetStarter 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | org.xolstice.maven.plugins 49 | protobuf-maven-plugin 50 | 0.5.1 51 | 52 | 53 | 54 | compile 55 | compile-custom 56 | 57 | 58 | 59 | 60 | com.google.protobuf:protoc:3.13.0:exe:${os.detected.classifier} 61 | grpc-java 62 | io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier} 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/TaskExecutors.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import cn.yang.common.exception.TaskExecutorException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.concurrent.*; 8 | 9 | /** 10 | * @author Cool-Coding 11 | * 2018/7/25 12 | */ 13 | public class TaskExecutors { 14 | /** logger */ 15 | private static final Logger LOGGER = LoggerFactory.getLogger(TaskExecutors.class); 16 | 17 | /** 18 | * 计划线程池 19 | */ 20 | private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors()+1); 21 | 22 | /** 23 | * 提交任务执行 24 | * @param callable 任务 25 | * @param interval 重试间隔 26 | * @param times 重试次数 27 | * @param 任务结果类型 28 | * @return 任务执行结果 29 | * @throws TaskExecutorException 任务异常 30 | */ 31 | public static T submit(Callable callable,long interval,int times) throws TaskExecutorException{ 32 | ThreadLocal count=new ThreadLocal<>(); 33 | count.set(1); 34 | //第一次立即执行 35 | ScheduledFuture schedule = SCHEDULED_EXECUTOR_SERVICE.schedule(callable, 0, TimeUnit.MICROSECONDS); 36 | //执行失败,则重试 37 | try { 38 | if (schedule.get() == null) { 39 | while (count.get() <= times) { 40 | schedule = SCHEDULED_EXECUTOR_SERVICE.schedule(callable, interval, TimeUnit.MILLISECONDS); 41 | //注意:要想重试,callable中的返回值必须null 42 | if (schedule.get() != null) { 43 | return schedule.get(); 44 | } 45 | count.set(count.get()+1); 46 | } 47 | }else{ 48 | return schedule.get(); 49 | } 50 | }catch (ExecutionException | InterruptedException e){ 51 | LOGGER.error(e.getMessage(),e); 52 | throw new TaskExecutorException(e.getMessage(),e); 53 | }finally { 54 | //清除记数 55 | count.remove(); 56 | } 57 | 58 | return null; 59 | } 60 | 61 | public static void submit(Runnable task,long delay){ 62 | SCHEDULED_EXECUTOR_SERVICE.schedule(task,delay,TimeUnit.MILLISECONDS); 63 | } 64 | 65 | public static void submit(Runnable task,long delay,long period){ 66 | SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(task,delay,period,TimeUnit.MILLISECONDS); 67 | } 68 | 69 | public static void shutdown(){ 70 | SCHEDULED_EXECUTOR_SERVICE.shutdown(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /common/common.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/netty/PuppetNettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.netty; 2 | 3 | import cn.yang.common.command.handler.ICommandHandler; 4 | import cn.yang.common.dto.Response; 5 | import cn.yang.common.util.CommandHandlerLoader; 6 | import cn.yang.common.util.PropertiesUtil; 7 | import cn.yang.puppet.client.constant.ConfigConstants; 8 | import io.netty.channel.ChannelHandler; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import io.netty.channel.SimpleChannelInboundHandler; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.util.StringUtils; 14 | 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | /** 19 | * @author Cool-Coding 20 | * 2018/7/25 21 | */ 22 | @ChannelHandler.Sharable 23 | public class PuppetNettyClientHandler extends SimpleChannelInboundHandler { 24 | /** logger */ 25 | private static final Logger LOGGER = LoggerFactory.getLogger(PuppetNettyClientHandler.class); 26 | private static final Map ERROR_COUNT_HASHMAP=new ConcurrentHashMap<>(); 27 | private static final int ERROR_COUNT=PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH,ConfigConstants.ERROR_COUNT,50); 28 | 29 | @SuppressWarnings("unchecked") 30 | @Override 31 | protected void channelRead0(ChannelHandlerContext ctx, Response response) throws Exception { 32 | LOGGER.debug(response.toString()); 33 | if (response.getError()!=null){ 34 | String message=response.getError().getMessage(); 35 | if(!StringUtils.isEmpty(message)) { 36 | LOGGER.error(message); 37 | if (!ERROR_COUNT_HASHMAP.containsKey(message)){ 38 | ERROR_COUNT_HASHMAP.put(message,1); 39 | return; 40 | } 41 | 42 | //判断是大于限制的次数,关闭与服务器之间的连接(比如控制端停止了控制,而傀儡没有收到命令,则会一直发送屏幕截图) 43 | //超过错误次数,关闭连接,重新与服务器连接,在连接时,服务器进行判断是否需要停止发送屏幕截图 44 | if(ERROR_COUNT_HASHMAP.get(message) >= ERROR_COUNT){ 45 | ctx.close(); 46 | return; 47 | } 48 | 49 | //记数加1 50 | ERROR_COUNT_HASHMAP.put(message,ERROR_COUNT_HASHMAP.get(message)+1); 51 | } 52 | return; 53 | } 54 | 55 | final ICommandHandler commandHandler = CommandHandlerLoader.getCommandHandler(response.getCommand()); 56 | if(commandHandler!=null) { 57 | commandHandler.handle(ctx, response); 58 | } 59 | } 60 | 61 | @Override 62 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 63 | LOGGER.error(cause.getMessage(),cause); 64 | ctx.close(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /puppet/src/test/java/GoRobotReplayTest.java: -------------------------------------------------------------------------------- 1 | import cn.yang.common.util.ImageUtils; 2 | import cn.yang.puppet.client.PuppetStarter; 3 | import com.google.protobuf.ByteString; 4 | import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; 5 | import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; 6 | import service.GoRobotGrpc; 7 | import service.GoRobotOuterClass; 8 | 9 | import java.awt.image.BufferedImage; 10 | import java.net.URL; 11 | import java.util.ArrayList; 12 | import java.util.Iterator; 13 | import java.util.List; 14 | 15 | public class GoRobotReplayTest { 16 | 17 | public static void getImage(String[] args) { 18 | io.grpc.Channel goRobotChannel; 19 | goRobotChannel = NettyChannelBuilder.forAddress("127.0.0.1", 12345) 20 | .negotiationType(NegotiationType.PLAINTEXT) 21 | .maxInboundMetadataSize(10 * 1024 *1024) 22 | .build(); 23 | 24 | //截图(截取整个屏幕图片) 25 | GoRobotOuterClass.CaptureScreenRequest captureScreenRequest = 26 | GoRobotOuterClass.CaptureScreenRequest 27 | .newBuilder() 28 | .setWidth(0) 29 | .setHeight(0) 30 | .build(); 31 | Iterator captureScreenResponseIterator = GoRobotGrpc 32 | .newBlockingStub(goRobotChannel) 33 | .captureScreen(captureScreenRequest); 34 | 35 | List byteList = new ArrayList<>(); 36 | while (captureScreenResponseIterator.hasNext()) { 37 | ByteString bitmap = captureScreenResponseIterator.next().getBitmap(); 38 | byte[] bytes = bitmap.toByteArray(); 39 | for (byte b : bytes) { 40 | byteList.add(b); 41 | } 42 | } 43 | 44 | byte[] bitMapBits = new byte[byteList.size()]; 45 | for (int i = 0; i < byteList.size(); i++) { 46 | bitMapBits[i] = byteList.get(i); 47 | } 48 | 49 | 50 | System.out.println(bitMapBits.length); 51 | // ImageUtils.saveImage(ImageUtils.getImageFromByteArray(bitMapBits),"E:/robot_win.png",ImageUtils.IMAGE_PNG); 52 | // BufferedImage bufferedImage = ImageUtils.convertByteArrayToImage(); 53 | BufferedImage imageFromByteArray = ImageUtils.getImageFromByteArray(bitMapBits); 54 | byte[] bytes1 = ImageUtils.ConvertImageToByteArray(imageFromByteArray,"jpg"); 55 | System.out.println(bytes1.length); 56 | byte[] bytes = ImageUtils.compressedImageAndGetByteArray(ImageUtils.getImageFromByteArray(bitMapBits), 0.8f); 57 | System.out.println(bytes.length); 58 | } 59 | 60 | public static void main(String[] args) { 61 | URL xmlpath = PuppetStarter.class.getClassLoader().getResource(""); 62 | System.out.println(xmlpath); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/PropertiesUtil.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import cn.yang.common.exception.ConfigParseException; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.util.HashMap; 9 | import java.util.Properties; 10 | 11 | /** 12 | * @author Cool-Coding 13 | * 2018/7/25 14 | */ 15 | public final class PropertiesUtil { 16 | private static final HashMap propertiesMap = new HashMap<>(); 17 | /** 18 | * logger 19 | */ 20 | private static final Logger LOGGER = LoggerFactory.getLogger(PropertiesUtil.class); 21 | 22 | public static int getInt(String filePath, String key, int defaultValue) { 23 | try { 24 | checkLoadConfigurationFile(filePath); 25 | return Integer.parseInt(getValue(filePath, key, String.valueOf(defaultValue))); 26 | }catch (IOException e) { 27 | return defaultValue; 28 | } 29 | } 30 | 31 | public static int getInt(String filePath, String key) { 32 | try { 33 | checkLoadConfigurationFile(filePath); 34 | return Integer.parseInt(getValue(filePath, key)); 35 | } catch (Exception e) { 36 | throw new ConfigParseException(e.getMessage(), e.getCause()); 37 | } 38 | } 39 | 40 | public static String getString(String filePath, String key, String defaultValue) { 41 | try { 42 | checkLoadConfigurationFile(filePath); 43 | } catch (IOException e) { 44 | return defaultValue; 45 | } 46 | return getValue(filePath, key, defaultValue); 47 | } 48 | 49 | public static String getString(String filePath, String key) { 50 | try { 51 | checkLoadConfigurationFile(filePath); 52 | } catch (IOException e) { 53 | throw new ConfigParseException(e.getMessage(), e.getCause()); 54 | } 55 | return getValue(filePath, key); 56 | } 57 | 58 | private static void checkLoadConfigurationFile(String filePath) throws IOException { 59 | if (propertiesMap.get(filePath) == null) { 60 | Properties properties = new Properties(); 61 | 62 | try { 63 | properties.load(PropertiesUtil.class.getClassLoader().getResourceAsStream(filePath)); 64 | } catch (IOException e) { 65 | LOGGER.error(e.getMessage(), e); 66 | throw e; 67 | } 68 | propertiesMap.put(filePath, properties); 69 | } 70 | } 71 | 72 | private static String getValue(String filePath, String key, String defaultValue) { 73 | return propertiesMap.get(filePath).getProperty(key, defaultValue); 74 | } 75 | 76 | private static String getValue(String filePath, String key) { 77 | return propertiesMap.get(filePath).getProperty(key); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /master/master.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | file://$MODULE_DIR$/src/main/resources/master-beans.xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /server/server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | file://$MODULE_DIR$/src/main/resources/server-beans.xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/artifacts/desktop_control_master.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/desktop_control_master 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.idea/artifacts/desktop_control_puppet.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $PROJECT_DIR$/out/artifacts/desktop_control_puppet 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/SerializationUtil.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import com.dyuproject.protostuff.LinkedBuffer; 4 | import com.dyuproject.protostuff.ProtostuffIOUtil; 5 | import com.dyuproject.protostuff.Schema; 6 | import com.dyuproject.protostuff.runtime.RuntimeSchema; 7 | import org.objenesis.Objenesis; 8 | import org.objenesis.ObjenesisStd; 9 | 10 | import java.io.*; 11 | import java.util.Map; 12 | import java.util.concurrent.ConcurrentHashMap; 13 | 14 | /** 15 | * @author: cool coding 16 | * @date: 2018/1/7 17 | * Time:21:37 18 | * Protostuff序列化与反序列化工具 19 | * Objenesis 来实例化对象,它是比 Java 反射更加强大 20 | */ 21 | public class SerializationUtil { 22 | 23 | private static Map,Schema> cachedSchema=new ConcurrentHashMap<>(); 24 | private static Objenesis objenesis = new ObjenesisStd(true); 25 | private SerializationUtil() { 26 | } 27 | 28 | @SuppressWarnings("unchecked") 29 | private static Schema getSchema(Class cls){ 30 | Schema schema=(Schema)cachedSchema.get(cls); 31 | if(schema==null){ 32 | schema= RuntimeSchema.createFrom(cls); 33 | if(schema != null){ 34 | cachedSchema.put(cls,schema); 35 | } 36 | } 37 | return schema; 38 | } 39 | 40 | @SuppressWarnings("unchecked") 41 | public static byte[] serialize(T obj){ 42 | Class cls=(Class)obj.getClass(); 43 | LinkedBuffer buffer= LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE); 44 | try{ 45 | Schema schema=getSchema(cls); 46 | return ProtostuffIOUtil.toByteArray(obj,schema,buffer); 47 | }catch(Exception e){ 48 | throw new IllegalStateException(e.getMessage(),e); 49 | }finally { 50 | buffer.clear(); 51 | } 52 | } 53 | 54 | public static byte[] JDKSerialize(T obj) throws IOException{ 55 | final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); 56 | final ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); 57 | objectOutputStream.writeObject(obj); 58 | return byteArrayOutputStream.toByteArray(); 59 | } 60 | 61 | public static T deSerialize(byte[] data,Class cls){ 62 | try{ 63 | T message = objenesis.newInstance(cls); 64 | Schema schema = getSchema(cls); 65 | ProtostuffIOUtil.mergeFrom(data, message, schema); 66 | return message; 67 | }catch (Exception e){ 68 | throw new IllegalStateException(e.getMessage(),e); 69 | } 70 | } 71 | 72 | public static T JDKDeSerialize(byte[] data,Class cls) throws IOException,ClassNotFoundException{ 73 | final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data); 74 | final ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); 75 | return (T)objectInputStream.readObject(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/netty/PuppetNettyClient.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.netty; 2 | 3 | import cn.yang.common.netty.ChannelInitializer; 4 | import cn.yang.common.netty.INettyClient; 5 | import cn.yang.common.util.PropertiesUtil; 6 | import cn.yang.common.util.TaskExecutors; 7 | import cn.yang.puppet.client.commandhandler.AbstractPuppetCommandHandler; 8 | import cn.yang.puppet.client.constant.ConfigConstants; 9 | import cn.yang.puppet.client.constant.ExceptionMessageConstants; 10 | import cn.yang.puppet.client.exception.PuppetClientException; 11 | import io.netty.bootstrap.Bootstrap; 12 | import io.netty.channel.ChannelFuture; 13 | import io.netty.channel.ChannelOption; 14 | import io.netty.channel.nio.NioEventLoopGroup; 15 | import io.netty.channel.socket.nio.NioSocketChannel; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | 19 | /** 20 | * @author Cool-Coding 21 | * 2018/7/24 22 | */ 23 | public class PuppetNettyClient implements INettyClient { 24 | /** 25 | * 处理器初始化器 26 | */ 27 | private ChannelInitializer channelInitialize; 28 | 29 | private NioEventLoopGroup group; 30 | 31 | /** logger */ 32 | private static final Logger LOGGER = LoggerFactory.getLogger(PuppetNettyClient.class); 33 | 34 | private String host; 35 | private int port; 36 | 37 | //与服务器的连结次数 38 | private int connectionCount=1; 39 | 40 | public void init() throws PuppetClientException{ 41 | group = new NioEventLoopGroup(); 42 | host = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_IP); 43 | port = PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_PORT); 44 | } 45 | 46 | @Override 47 | public void connect(){ 48 | final Bootstrap bootstrap = new Bootstrap(); 49 | bootstrap.group(group) 50 | .channel(NioSocketChannel.class) 51 | .option(ChannelOption.TCP_NODELAY, true) 52 | .handler(channelInitialize); 53 | if (channelInitialize.getChannelHandler() instanceof PuppetNettyClientHandler) { 54 | try { 55 | final ChannelFuture sync = bootstrap.connect(host, port).sync(); 56 | sync.channel().writeAndFlush(AbstractPuppetCommandHandler.buildConnectionRequest(connectionCount++)); 57 | sync.channel().closeFuture().sync(); 58 | } catch (Exception e) { 59 | LOGGER.error(e.getMessage(), e); 60 | } finally { 61 | //如果连接断开了,重新与服务器连接 62 | int interval=PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.RECONNECT_INTERVAL); 63 | LOGGER.error(ExceptionMessageConstants.DISCONNECT_TO_SERVER, host, port,interval); 64 | TaskExecutors.submit(this::connect,interval ); 65 | } 66 | }else { 67 | throw new RuntimeException(ExceptionMessageConstants.PUPPET_HANDLER_ERROR); 68 | } 69 | 70 | } 71 | 72 | public void setChannelInitialize(ChannelInitializer channelInitialize) { 73 | this.channelInitialize = channelInitialize; 74 | } 75 | 76 | public void destroy(){ 77 | group.shutdownGracefully(); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/netty/MasterNettyClientHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.netty; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.command.handler.ICommandHandler; 5 | import cn.yang.common.dto.Response; 6 | import cn.yang.common.exception.CommandHandlerLoaderException; 7 | import cn.yang.common.util.CommandHandlerLoader; 8 | import cn.yang.master.client.commandhandler.AbstractMasterFireCommandHandler; 9 | import cn.yang.master.client.constant.ExceptionMessageConstants; 10 | import cn.yang.master.client.exception.FireCommandHandlerException; 11 | import cn.yang.master.client.exception.MasterChannelHandlerException; 12 | import io.netty.channel.ChannelHandler; 13 | import io.netty.channel.ChannelHandlerContext; 14 | import io.netty.channel.SimpleChannelInboundHandler; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | import org.springframework.util.StringUtils; 18 | 19 | import javax.swing.*; 20 | 21 | /** 22 | * @author Cool-Coding 23 | * 2018/7/25 24 | */ 25 | @ChannelHandler.Sharable 26 | public class MasterNettyClientHandler extends SimpleChannelInboundHandler { 27 | /** logger */ 28 | private static final Logger LOGGER = LoggerFactory.getLogger(MasterNettyClientHandler.class); 29 | 30 | //记录上一次消息 31 | private String pre_message; 32 | 33 | @SuppressWarnings("unchecked") 34 | @Override 35 | protected void channelRead0(ChannelHandlerContext ctx, Response response) throws Exception { 36 | if (response.getError()!=null ){ 37 | if(!response.getError().getMessage().equals(pre_message)) { 38 | SwingUtilities.invokeLater(() -> { 39 | JOptionPane.showMessageDialog(null, response.getError().getMessage()); 40 | }); 41 | pre_message = response.getError().getMessage(); 42 | LOGGER.error(pre_message); 43 | } 44 | return; 45 | } 46 | 47 | final ICommandHandler commandHandler = CommandHandlerLoader.getCommandHandler(response.getCommand()); 48 | LOGGER.debug(response.toString()); 49 | commandHandler.handle(ctx,response); 50 | } 51 | 52 | @Override 53 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 54 | LOGGER.error(cause.getMessage(),cause); 55 | ctx.close(); 56 | } 57 | 58 | @SuppressWarnings("unchecked") 59 | void fireCommand(String puppetName, Enum command, Object data){ 60 | if (StringUtils.isEmpty(puppetName)){ 61 | throw new MasterChannelHandlerException(ExceptionMessageConstants.PUPPET_NAME_EMPTY); 62 | } 63 | 64 | try { 65 | getFireCommandHandler(command).fire(puppetName,command,data); 66 | LOGGER.debug("fire a command to server:{}",command); 67 | } catch (FireCommandHandlerException e) { 68 | throw new MasterChannelHandlerException(e.getMessage(), e); 69 | } 70 | } 71 | 72 | 73 | @SuppressWarnings("unchecked") 74 | private AbstractMasterFireCommandHandler getFireCommandHandler(Enum command) throws MasterChannelHandlerException{ 75 | try { 76 | final ICommandHandler commandHandler = CommandHandlerLoader.getCommandHandler(command); 77 | if (commandHandler instanceof AbstractMasterFireCommandHandler) { 78 | return (AbstractMasterFireCommandHandler) commandHandler; 79 | } else { 80 | throw new MasterChannelHandlerException(ExceptionMessageConstants.FIRE_COMMAND_HANDLE_ERROR); 81 | } 82 | }catch (CommandHandlerLoaderException e){ 83 | throw new MasterChannelHandlerException(e.getMessage(),e); 84 | } 85 | 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/AbstractServerCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.command.handler.ICommandHandler; 5 | import cn.yang.common.dto.Request; 6 | import cn.yang.common.dto.Response; 7 | import cn.yang.common.exception.ServerException; 8 | import cn.yang.server.netty.ChannelPair; 9 | import io.netty.channel.ChannelHandlerContext; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.util.StringUtils; 13 | 14 | import java.util.Arrays; 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | 18 | import static cn.yang.common.constant.ExceptionMessageConstants.REQUIRED_PUPPET_NAME; 19 | import static cn.yang.common.constant.ExceptionMessageConstants.REQUIRED_REQUESTID; 20 | 21 | /** 22 | * @author Cool-Coding 23 | * 2018/7/27 24 | */ 25 | public abstract class AbstractServerCommandHandler implements ICommandHandler{ 26 | 27 | /** logger */ 28 | private static Logger LOGGER; 29 | 30 | /** 31 | * 已经建立连接的控制端(指当前正控制傀儡中)与傀儡端(与服务器连接中) 32 | * key:傀儡名称(傀儡名称唯一) 33 | * value:控制端与傀儡的channel 34 | */ 35 | protected static final Map CONNECTED_CHANNELPAIRS=new ConcurrentHashMap<>(); 36 | 37 | 38 | public AbstractServerCommandHandler(){ 39 | //根据不同的子类,记录的日志类名不一样 40 | LOGGER=LoggerFactory.getLogger(this.getClass()); 41 | } 42 | 43 | @Override 44 | public void handle(ChannelHandlerContext ctx, Request request) throws Exception { 45 | if(StringUtils.isEmpty(request.getId())){ 46 | error(request,REQUIRED_REQUESTID); 47 | sendError(request,ctx,REQUIRED_REQUESTID); 48 | return; 49 | } 50 | 51 | /* 52 | * 除了连接时不需要傀儡名,其它情况都需要 53 | */ 54 | if(!(request.getCommand()== Commands.CONNECT)){ 55 | if(StringUtils.isEmpty(request.getPuppetName())) { 56 | error(request,REQUIRED_PUPPET_NAME); 57 | sendError(request, ctx, REQUIRED_PUPPET_NAME); 58 | return; 59 | } 60 | } 61 | 62 | handle0(ctx,request); 63 | } 64 | 65 | protected void sendError(Request request, ChannelHandlerContext ctx, String e){ 66 | Response response=new Response(); 67 | response.setPuppetName(request.getPuppetName()); 68 | response.setId(request.getId()); 69 | response.setError(new ServerException(e)); 70 | ctx.writeAndFlush(response); 71 | } 72 | 73 | 74 | protected Response buildResponse(Request request,Enum command){ 75 | return buildResponse(request,command,null); 76 | } 77 | 78 | protected Response buildResponse(Request request,Enum command, Object result){ 79 | Response response=new Response(); 80 | response.setId(request.getId()); 81 | response.setPuppetName(request.getPuppetName()); 82 | response.setCommand(command); 83 | response.setValue(result); 84 | return response; 85 | } 86 | 87 | void error(Request request,String... message){ 88 | LOGGER.error("{}:{}",request, Arrays.toString(message)); 89 | } 90 | 91 | void debug(Request request,String... message){ 92 | LOGGER.debug("{}:{}",request, Arrays.toString(message)); 93 | } 94 | 95 | void info(Request request,String... message){ 96 | LOGGER.info("{}:{}",request, Arrays.toString(message)); 97 | } 98 | 99 | void warn(Request request,String... message){ 100 | LOGGER.warn("{}:{}",request, Arrays.toString(message)); 101 | } 102 | 103 | public abstract void handle0(ChannelHandlerContext ctx, Request request) throws Exception; 104 | } 105 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/ControlCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.dto.Request; 5 | import cn.yang.common.exception.ServerException; 6 | import cn.yang.common.exception.TaskExecutorException; 7 | import cn.yang.common.util.PropertiesUtil; 8 | import cn.yang.common.util.TaskExecutors; 9 | import cn.yang.server.netty.ChannelPair; 10 | import io.netty.channel.Channel; 11 | import io.netty.channel.ChannelHandlerContext; 12 | 13 | import static cn.yang.common.constant.ExceptionMessageConstants.CONNECTION_EXIST; 14 | import static cn.yang.common.constant.ExceptionMessageConstants.CONNECT_PUPPET_FAILED; 15 | import static cn.yang.server.constant.ConfigConstants.*; 16 | import static cn.yang.server.constant.MessageConstants.CONNECTION_SUCCEED; 17 | 18 | /** 19 | * @author Cool-Coding 20 | * 2018/7/27 21 | */ 22 | public class ControlCommandHandler extends AbstractServerCommandHandler { 23 | @Override 24 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 25 | final String puppetName = request.getPuppetName(); 26 | //根据傀儡名称得到channel 27 | try { 28 | Boolean result=notifyPuppetConnect(ctx,puppetName); 29 | if(result==null || !result){ 30 | info(request,CONNECT_PUPPET_FAILED); 31 | sendError(request,ctx,CONNECT_PUPPET_FAILED); 32 | return; 33 | } 34 | 35 | final ChannelPair channelPair = CONNECTED_CHANNELPAIRS.get(puppetName); 36 | if (channelPair != null) { 37 | final Channel masterChannel = channelPair.getMasterChannel(); 38 | if (masterChannel!=null && masterChannel.isOpen()){ 39 | info(request,CONNECT_PUPPET_FAILED,CONNECTION_EXIST); 40 | sendError(request,ctx,CONNECT_PUPPET_FAILED); 41 | }else { 42 | channelPair.setMasterChannel(ctx.channel()); 43 | info(request, CONNECTION_SUCCEED); 44 | //通知傀儡 45 | channelPair.getPuppetChannel().writeAndFlush(buildResponse(request, Commands.CONTROL)); 46 | //给控制端返回消息,通知其做好准备 47 | ctx.writeAndFlush(buildResponse(request, Commands.CONTROL, puppetName)); 48 | } 49 | } 50 | 51 | }catch (TaskExecutorException e){ 52 | error(request,CONNECT_PUPPET_FAILED,e.getMessage()); 53 | throw new ServerException(e.getMessage(),e); 54 | } 55 | } 56 | 57 | 58 | /** 59 | * 通知傀儡建立连接 60 | * @param name 傀儡名 61 | * @return 通知结果 62 | * @throws TaskExecutorException 任务执行异常 63 | */ 64 | private Boolean notifyPuppetConnect(ChannelHandlerContext ctx,String name) throws TaskExecutorException{ 65 | int interval= PropertiesUtil.getInt(CONFIG_FILE_PATH, MASTER_CONNECT_PUPPET_RETRY_INTERVAL,1000); 66 | int times=PropertiesUtil.getInt(CONFIG_FILE_PATH, MASTER_CONNECT_PUPPET_RETRY_TIMES,1); 67 | 68 | return TaskExecutors.submit(()->{ 69 | //1.检查已连接的傀儡中是否有目标傀儡 70 | if (CONNECTED_CHANNELPAIRS.containsKey(name)) { 71 | final ChannelPair pair = CONNECTED_CHANNELPAIRS.get(name); 72 | final Channel masterChannel = pair.getMasterChannel(); 73 | //如果当前傀儡已经在被不同的控制端控制,则拒绝 74 | if(masterChannel!=null && ctx.channel()!=masterChannel && masterChannel.isOpen()){ 75 | return false; 76 | } 77 | 78 | final Channel puppetChannel = pair.getPuppetChannel(); 79 | if (puppetChannel != null && puppetChannel.isOpen()) { 80 | return true; 81 | } 82 | } 83 | 84 | //2.返回null时,会重试 85 | return null; 86 | },interval,times); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/impl/AbstractDisplayPuppet.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui.impl; 2 | 3 | import cn.yang.common.util.BeanUtil; 4 | import cn.yang.master.client.ui.IDisplayPuppet; 5 | import cn.yang.master.client.ui.IMasterDesktop; 6 | import cn.yang.master.client.ui.listener.KeyBoardListener; 7 | import cn.yang.master.client.ui.listener.MouseListener; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.WindowAdapter; 12 | import java.awt.event.WindowEvent; 13 | 14 | /** 15 | * @author Cool-Coding 16 | * 2018/8/2 17 | */ 18 | public abstract class AbstractDisplayPuppet implements IDisplayPuppet { 19 | protected final IMasterDesktop masterDesktop = BeanUtil.getBean(IMasterDesktop.class); 20 | 21 | /** 22 | * 窗体 23 | */ 24 | private final JFrame jFrame; 25 | 26 | /** 27 | * 傀儡名 28 | */ 29 | protected final String puppetName; 30 | 31 | /** 32 | * 傀儡屏幕 33 | */ 34 | private CanvasPanel imageJpanel; 35 | 36 | AbstractDisplayPuppet(String puppetName){ 37 | this.puppetName=puppetName; 38 | this.jFrame=new JFrame(); 39 | 40 | setting(); 41 | initBody(); 42 | initMenu(jFrame); 43 | } 44 | 45 | /** 46 | * 窗体属性设置 47 | */ 48 | private void setting(){ 49 | jFrame.setLocation(250, 250); 50 | jFrame.setSize(1000,800); 51 | jFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 52 | jFrame.addWindowListener(new WindowAdapter() { 53 | @Override 54 | public void windowClosing(WindowEvent e) { 55 | masterDesktop.terminate(puppetName); 56 | } 57 | }); 58 | } 59 | 60 | /** 61 | * 初使化主体 62 | */ 63 | private void initBody(){ 64 | imageJpanel=new CanvasPanel(); 65 | jFrame.add(imageJpanel); 66 | final KeyBoardListener keyBoardListener = new KeyBoardListener(AbstractDisplayPuppet.this); 67 | final MouseListener mouseListener = new MouseListener(AbstractDisplayPuppet.this); 68 | jFrame.addKeyListener(keyBoardListener); 69 | imageJpanel.addMouseListener(mouseListener); 70 | // imageJpanel.addMouseMotionListener(mouseListener); 71 | imageJpanel.addMouseWheelListener(mouseListener); 72 | } 73 | 74 | @Override 75 | public void launch() { 76 | SwingUtilities.invokeLater(()->{ 77 | jFrame.setVisible(true); 78 | }); 79 | } 80 | 81 | @Override 82 | public void refresh(byte[] bytes) { 83 | generateImage(bytes); 84 | SwingUtilities.invokeLater(() -> { 85 | this.imageJpanel.repaint(); 86 | }); 87 | } 88 | 89 | /** 90 | * 远程桌面 91 | */ 92 | private class CanvasPanel extends JPanel { 93 | private static final long serialVersionUID = -3313907120784874523L; 94 | 95 | @Override 96 | protected void paintComponent(Graphics g) { 97 | super.paintComponent(g); 98 | AbstractDisplayPuppet.this.paintComponent(g); 99 | } 100 | 101 | @Override 102 | public void paint(Graphics g) { 103 | super.paint(g); 104 | AbstractDisplayPuppet.this.paint(g); 105 | } 106 | } 107 | 108 | /** 109 | * 由子类具体的画图逻辑 110 | * @param g 绘图器 111 | */ 112 | protected void paint(Graphics g){}; 113 | 114 | protected void paintComponent(Graphics g){}; 115 | /** 116 | * 窗口引用,用于子类进行菜单功能扩展 117 | * @return 118 | */ 119 | public JFrame getjFrame() { 120 | return jFrame; 121 | } 122 | 123 | 124 | /** 125 | * 处理字节数组,生成bufferedImage或像素,由子类决定 126 | * @param bytes 127 | */ 128 | abstract void generateImage(byte[] bytes); 129 | 130 | /** 131 | * 初始化菜单,由子类实现 132 | * @param jFrame JFrame窗体 133 | */ 134 | abstract void initMenu(JFrame jFrame); 135 | 136 | 137 | @Override 138 | public String getPuppetName() { 139 | return puppetName; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /server/src/main/java/cn/yang/server/commandhandler/ConnectionCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.server.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.constant.Constants; 5 | import cn.yang.common.dto.Request; 6 | import cn.yang.common.dto.Response; 7 | import cn.yang.common.generator.PuppetNameGenerate; 8 | import cn.yang.common.util.BeanUtil; 9 | import cn.yang.server.netty.ChannelPair; 10 | import io.netty.channel.Channel; 11 | import io.netty.channel.ChannelHandlerContext; 12 | import org.springframework.util.StringUtils; 13 | 14 | import static cn.yang.common.constant.ExceptionMessageConstants.*; 15 | import static cn.yang.server.constant.MessageConstants.CONNECTION_SUCCEED; 16 | 17 | /** 18 | * @author Cool-Coding 19 | * 2018/7/27 20 | */ 21 | public class ConnectionCommandHandler extends AbstractServerCommandHandler { 22 | @Override 23 | public void handle0(ChannelHandlerContext ctx, Request request) throws Exception { 24 | String requestId=request.getId(); 25 | switch (requestId.charAt(0)){ 26 | case Constants.MASTER: 27 | handleMaster(ctx,request); 28 | break; 29 | case Constants.PUPPET: 30 | handlePuppet(ctx,request); 31 | break; 32 | default: 33 | error(request,WRONG_CLIENT_TYPE); 34 | sendError(request,ctx,WRONG_CLIENT_TYPE); 35 | } 36 | } 37 | 38 | private void handleMaster(ChannelHandlerContext ctx,Request request){ 39 | Response response = buildResponse(request, Commands.CONNECT); 40 | ctx.writeAndFlush(response); 41 | } 42 | 43 | private void handlePuppet(ChannelHandlerContext ctx,Request request){ 44 | String puppetName = request.getPuppetName(); 45 | if(!StringUtils.isEmpty(puppetName)){ 46 | if(request.getValue()!=null){ 47 | if (request.getValue() instanceof Integer){ 48 | Integer count = (Integer)request.getValue(); 49 | if (count <= 1){ 50 | error(request,ILLEGAL_STATUS); 51 | sendError(request,ctx,ILLEGAL_STATUS); 52 | return; 53 | } 54 | } 55 | } 56 | 57 | ChannelPair channelPair; 58 | if(!CONNECTED_CHANNELPAIRS.containsKey(puppetName)){ 59 | channelPair=new ChannelPair(); 60 | channelPair.setPuppetChannel(ctx.channel()); 61 | CONNECTED_CHANNELPAIRS.put(puppetName,channelPair); 62 | }else { 63 | channelPair = CONNECTED_CHANNELPAIRS.get(puppetName); 64 | final Channel puppetChannel = channelPair.getPuppetChannel(); 65 | if(puppetChannel!=null && puppetChannel.isOpen()){ 66 | error(request,CONNECTION_EXIST); 67 | sendError(request,ctx,CONNECTION_EXIST); 68 | return; 69 | }else{ 70 | channelPair.setPuppetChannel(ctx.channel()); 71 | } 72 | } 73 | 74 | info(request,CONNECTION_SUCCEED); 75 | final Channel masterChannel = channelPair.getMasterChannel(); 76 | //如果傀儡掉线后,再次重连,发现控制端在线,并且没有终止控制傀儡,则继续发送屏幕截图 77 | if (masterChannel!=null && masterChannel.isOpen()){ 78 | ctx.writeAndFlush(buildResponse(request, Commands.CONTROL)); 79 | //否则,如果控制端不在线并且傀儡是断线重连的情况,则向其发送终止命令,停止其向服务器发送屏幕截图 80 | }else{ 81 | ctx.writeAndFlush(buildResponse(request, Commands.TERMINATE)); 82 | } 83 | }else { 84 | ChannelPair pair=new ChannelPair(); 85 | pair.setPuppetChannel(ctx.channel()); 86 | puppetName = BeanUtil.getBean(PuppetNameGenerate.class).getPuppetName(ctx); 87 | CONNECTED_CHANNELPAIRS.put(puppetName,pair); 88 | request.setPuppetName(puppetName); 89 | info(request,CONNECTION_SUCCEED); 90 | } 91 | 92 | Response response = buildResponse(request, Commands.CONNECT,request.getValue()); 93 | ctx.writeAndFlush(response); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/robot/JavaRobotReplay.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.robot; 2 | 3 | 4 | import cn.yang.common.InputEvent.MasterKeyEvent; 5 | import cn.yang.common.InputEvent.MasterMouseEvent; 6 | import cn.yang.common.util.ImageUtils; 7 | import cn.yang.puppet.client.constant.PuppetDynamicSetting; 8 | import cn.yang.puppet.client.ui.IReplay; 9 | 10 | import java.awt.*; 11 | import java.awt.event.KeyEvent; 12 | import java.awt.image.BufferedImage; 13 | 14 | /** 15 | * @author Cool-Coding 16 | * 2018/7/25 17 | */ 18 | public class JavaRobotReplay implements IReplay { 19 | /** 20 | * 控制器 21 | */ 22 | private Robot robot; 23 | private Toolkit toolkit; 24 | 25 | public JavaRobotReplay(){ 26 | try { 27 | robot=new Robot(); 28 | toolkit=Toolkit.getDefaultToolkit(); 29 | } catch (AWTException e) { 30 | throw new RuntimeException(e); 31 | } 32 | } 33 | 34 | @Override 35 | public void keyPress(MasterKeyEvent keyEvent){ 36 | if (keyEvent.isAltDown()){ 37 | robot.keyPress(KeyEvent.VK_ALT); 38 | } 39 | 40 | if (keyEvent.isCtrlDown()){ 41 | robot.keyPress(KeyEvent.VK_CONTROL); 42 | } 43 | 44 | if (keyEvent.isShiftDown()){ 45 | robot.keyPress(KeyEvent.VK_SHIFT); 46 | } 47 | 48 | robot.keyPress(keyEvent.getKeyCode()); 49 | } 50 | 51 | @Override 52 | public void keyRelease(MasterKeyEvent keyEvent){ 53 | if (keyEvent.isAltDown()){ 54 | robot.keyRelease(KeyEvent.VK_ALT); 55 | } 56 | 57 | if (keyEvent.isCtrlDown()){ 58 | robot.keyRelease(KeyEvent.VK_CONTROL); 59 | } 60 | 61 | if (keyEvent.isShiftDown()){ 62 | robot.keyRelease(KeyEvent.VK_SHIFT); 63 | } 64 | 65 | robot.keyRelease(keyEvent.getKeyCode()); 66 | } 67 | 68 | @Override 69 | public void mouseClick(MasterMouseEvent mouseEvent){ 70 | final int mouseButton1 = mouseEvent.getMouseButton(); 71 | robot.mousePress(mouseButton1); 72 | robot.mouseRelease(mouseButton1); 73 | } 74 | 75 | 76 | @Override 77 | public void mouseWheel(MasterMouseEvent mouseEvent){ 78 | robot.mouseWheel(mouseEvent.getMouseWheel()); 79 | } 80 | 81 | @Override 82 | public void mousePress(MasterMouseEvent mouseEvent){ 83 | System.out.println("mouse press:" + mouseEvent.getMouseButton()); 84 | robot.mousePress(mouseEvent.getMouseButton()); 85 | } 86 | 87 | @Override 88 | public void mouseRelease(MasterMouseEvent mouseEvent){ 89 | System.out.println("mouse released:" + mouseEvent.getMouseButton()); 90 | robot.mouseRelease(mouseEvent.getMouseButton()); 91 | } 92 | 93 | @Override 94 | public void mouseMove(int[] site){ 95 | System.out.println("mouse move:" + site[0] + ":" + site[1]); 96 | robot.mouseMove(site[0],site[1]); 97 | } 98 | 99 | @Override 100 | public void mouseDoubleClick(MasterMouseEvent mouseEvent){ 101 | final int mouseButton1 = mouseEvent.getMouseButton(); 102 | robot.mousePress(mouseButton1); 103 | robot.mouseRelease(mouseButton1); 104 | robot.mousePress(mouseButton1); 105 | robot.mouseRelease(mouseButton1); 106 | } 107 | 108 | @Override 109 | public void mouseDragged(MasterMouseEvent mouseEvent, int[] site){ 110 | final int mouseButton1 = mouseEvent.getMouseButton(); 111 | robot.mousePress(mouseButton1); 112 | mouseMove(site); 113 | } 114 | 115 | public Robot getRobot() { 116 | return robot; 117 | } 118 | 119 | @Override 120 | public byte[] getScreenSnapshot(){ 121 | //获取屏幕分辨率 122 | Dimension d = toolkit.getScreenSize(); 123 | //以屏幕的尺寸创建个矩形 124 | Rectangle screenRect = new Rectangle(d); 125 | //截图(截取整个屏幕图片) 126 | BufferedImage bufferedImage = robot.createScreenCapture(screenRect); 127 | return ImageUtils.compressedImageAndGetByteArray(bufferedImage, PuppetDynamicSetting.quality/100.0f); 128 | } 129 | } -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/netty/MasterNettyClient.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.netty; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.constant.Constants; 5 | import cn.yang.common.dto.Request; 6 | import cn.yang.common.generator.SequenceGenerate; 7 | import cn.yang.common.netty.ChannelInitializer; 8 | import cn.yang.common.netty.INettyClient; 9 | import cn.yang.common.util.BeanUtil; 10 | import cn.yang.common.util.MacUtils; 11 | import cn.yang.common.util.PropertiesUtil; 12 | import cn.yang.master.client.constant.ConfigConstants; 13 | import cn.yang.master.client.constant.ExceptionMessageConstants; 14 | import cn.yang.master.client.exception.MasterChannelHandlerException; 15 | import cn.yang.master.client.exception.MasterClientException; 16 | import io.netty.bootstrap.Bootstrap; 17 | import io.netty.channel.ChannelFuture; 18 | import io.netty.channel.ChannelOption; 19 | import io.netty.channel.nio.NioEventLoopGroup; 20 | import io.netty.channel.socket.nio.NioSocketChannel; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.springframework.util.StringUtils; 24 | 25 | /** 26 | * @author Cool-Coding 27 | * 2018/7/24 28 | * Netty客户端,负责控制端与服务器的通信,包括所有请求数据的发送接收 29 | */ 30 | public class MasterNettyClient implements INettyClient{ 31 | /** 32 | * 处理器初始化器 33 | */ 34 | private ChannelInitializer channelInitialize; 35 | 36 | private NioEventLoopGroup group; 37 | 38 | /** logger */ 39 | private static final Logger LOGGER = LoggerFactory.getLogger(MasterNettyClient.class); 40 | 41 | private String host; 42 | private int port; 43 | 44 | /** 45 | * 初始化 46 | */ 47 | public void init(){ 48 | group = new NioEventLoopGroup(); 49 | host = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_IP); 50 | port = PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH, ConfigConstants.SERVER_PORT); 51 | 52 | } 53 | 54 | /** 55 | * 启动时连接服务器 56 | * @throws Exception 57 | */ 58 | @Override 59 | public void connect() throws Exception{ 60 | final Bootstrap bootstrap = new Bootstrap(); 61 | bootstrap.group(group) 62 | .channel(NioSocketChannel.class) 63 | .option(ChannelOption.TCP_NODELAY, true) 64 | .handler(channelInitialize); 65 | final ChannelFuture sync = bootstrap.connect(host, port).sync(); 66 | sync.channel().writeAndFlush(buildConnectRequest()); 67 | try { 68 | sync.channel().closeFuture(); 69 | }catch (Exception e){ 70 | LOGGER.error(e.getMessage(),e); 71 | throw e; 72 | } 73 | } 74 | 75 | /** 76 | * 发送命令 77 | * @param puppetName 傀儡名 78 | * @param command 命令 79 | * @param data 数据 80 | * @throws MasterClientException 81 | */ 82 | public void fireCommand(String puppetName,Enum command,Object data) throws MasterClientException{ 83 | try { 84 | getChannelHandler().fireCommand(puppetName,command,data); 85 | }catch (MasterChannelHandlerException e){ 86 | throw new MasterClientException(e.getMessage(),e); 87 | } 88 | } 89 | 90 | 91 | private MasterNettyClientHandler getChannelHandler(){ 92 | if (channelInitialize.getChannelHandler() instanceof MasterNettyClientHandler) { 93 | return (MasterNettyClientHandler) channelInitialize.getChannelHandler(); 94 | } else { 95 | final String message = String.format("%s %s", ExceptionMessageConstants.HANDLER_NOT_SUPPORTED, channelInitialize.getChannelHandler()); 96 | LOGGER.error(message); 97 | throw new RuntimeException(message); 98 | } 99 | } 100 | 101 | private Request buildConnectRequest() throws MasterClientException{ 102 | String mac= MacUtils.getMAC(); 103 | if (StringUtils.isEmpty(mac)){ 104 | return null; 105 | } 106 | 107 | final SequenceGenerate generator = BeanUtil.getBean(SequenceGenerate.class); 108 | 109 | Request request = new Request(); 110 | request.setId(Constants.MASTER + mac + generator.next()); 111 | request.setCommand(Commands.CONNECT); 112 | return request; 113 | } 114 | 115 | public void setChannelInitialize(ChannelInitializer channelInitialize) { 116 | this.channelInitialize = channelInitialize; 117 | } 118 | 119 | public void destroy(){ 120 | group.shutdownGracefully(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/util/CommandHandlerLoader.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.util; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.command.handler.ICommandHandler; 5 | import cn.yang.common.constant.ExceptionMessageConstants; 6 | import cn.yang.common.exception.CommandHandlerLoaderException; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.util.StringUtils; 10 | 11 | import java.io.BufferedReader; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.util.Map; 15 | import java.util.concurrent.ConcurrentHashMap; 16 | 17 | import static cn.yang.common.constant.ExceptionMessageConstants.*; 18 | 19 | /** 20 | * @author Cool-Coding 21 | * 2018/7/27 22 | */ 23 | public class CommandHandlerLoader { 24 | 25 | /** logger */ 26 | private static final Logger LOGGER = LoggerFactory.getLogger(CommandHandlerLoader.class); 27 | 28 | private static final Map, ICommandHandler> HANDLERS =new ConcurrentHashMap<>(); 29 | 30 | private static final String COMMAND_HANDLER_PATH="META-INF/commandhandlers"; 31 | 32 | 33 | /** 34 | * 根据命令获取对应的ICommandHandler对象 35 | * @param command 命令 36 | * @return command handler 37 | */ 38 | public static ICommandHandler getCommandHandler(Enum command) throws CommandHandlerLoaderException { 39 | if(command==null){ 40 | return null; 41 | } 42 | 43 | //查询缓存 44 | final ICommandHandler iCommandHandler = HANDLERS.get(command); 45 | if (iCommandHandler!=null){ 46 | return iCommandHandler; 47 | } 48 | 49 | 50 | synchronized (CommandHandlerLoader.class) { 51 | //加载第一个为ICommandHandler类型的实现 52 | final ICommandHandler commandHandler = loadCommandHandler(command); 53 | HANDLERS.put(command, commandHandler); 54 | return commandHandler; 55 | } 56 | } 57 | 58 | private static ICommandHandler loadCommandHandler(Enum command) throws CommandHandlerLoaderException { 59 | final ICommandHandler iCommandHandler = HANDLERS.get(command); 60 | if (iCommandHandler!=null){ 61 | return iCommandHandler; 62 | } 63 | 64 | final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 65 | final InputStream resourceAsStream = classLoader.getResourceAsStream(COMMAND_HANDLER_PATH); 66 | if (resourceAsStream==null){ 67 | LOGGER.error(COMMANDHANDLERS_FILE_NOT_FOUND); 68 | throw new CommandHandlerLoaderException(COMMANDHANDLERS_FILE_NOT_FOUND); 69 | } 70 | 71 | try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resourceAsStream, "utf-8"))) { 72 | String s; 73 | while(!StringUtils.isEmpty(s=bufferedReader.readLine())){ 74 | final String[] split = s.split("="); 75 | if (split.length!=2){ 76 | LOGGER.error(COMMANDHANDLERS_FILE_CONFIG_ERROR); 77 | throw new CommandHandlerLoaderException(COMMANDHANDLERS_FILE_CONFIG_ERROR); 78 | } 79 | 80 | String key=split[0]; 81 | if (command.name().equals(key)) { 82 | String value = split[1]; 83 | final Class aClass = Class.forName(value); 84 | final Class inter = getSuperestInterface(aClass); 85 | if (inter == null) { 86 | throw new CommandHandlerLoaderException(COMMAND_HANDLER_ERROR); 87 | } 88 | return (ICommandHandler) aClass.newInstance(); 89 | } 90 | } 91 | throw new CommandHandlerLoaderException(String.format("%s %s",command.name(), ExceptionMessageConstants.COMMAND_HANDLER_NOT_FOUND)); 92 | }catch (Exception e){ 93 | LOGGER.error(e.getMessage(),e); 94 | throw new CommandHandlerLoaderException(e.getMessage(),e); 95 | } 96 | } 97 | 98 | private static Class getSuperestInterface(Class aClass){ 99 | //如果没有父类,则返回null 100 | if (aClass==null) { 101 | return null; 102 | } 103 | 104 | //获取类继承的接口 105 | final Class[] interfaces = aClass.getInterfaces(); 106 | 107 | //判断是否继承了接口ICommandHandler 108 | for(Class c:interfaces){ 109 | if (c==ICommandHandler.class){ 110 | return c; 111 | } 112 | } 113 | 114 | return getSuperestInterface(aClass.getSuperclass()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /puppet/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | desktop-control-parent 7 | cn.yang.remote 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | puppet 13 | 14 | 15 | 16 | cn.yang.remote 17 | common 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | net.java.dev.jna 23 | jna 24 | 5.3.1 25 | 26 | 27 | 28 | io.grpc 29 | grpc-netty-shaded 30 | 1.20.0 31 | 32 | 33 | io.grpc 34 | grpc-protobuf 35 | 1.20.0 36 | 37 | 38 | io.grpc 39 | grpc-stub 40 | 1.20.0 41 | 42 | 43 | com.google.protobuf 44 | protobuf-java 45 | 3.16.1 46 | 47 | 48 | 49 | 50 | 51 | puppet 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-compiler-plugin 57 | 3.8.1 58 | 59 | 1.8 60 | 1.8 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-shade-plugin 68 | 3.2.1 69 | 70 | 71 | package 72 | 73 | shade 74 | 75 | 76 | 77 | 79 | cn.yang.puppet.client.PuppetStarter 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | org.xolstice.maven.plugins 88 | protobuf-maven-plugin 89 | 0.5.1 90 | 91 | com.google.protobuf:protoc:3.13.0:exe:${os.detected.classifier} 92 | grpc-java 93 | io.grpc:protoc-gen-grpc-java:1.20.0:exe:${os.detected.classifier} 94 | 95 | 96 | 97 | 98 | compile 99 | compile-custom 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | kr.motd.maven 109 | os-maven-plugin 110 | 1.5.0.Final 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/commandhandler/AbstractMasterCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.command.handler.ICommandHandler; 5 | import cn.yang.common.constant.Constants; 6 | import cn.yang.common.constant.ExceptionMessageConstants; 7 | import cn.yang.common.dto.Request; 8 | import cn.yang.common.dto.Response; 9 | import cn.yang.common.exception.ResponseHandleException; 10 | import cn.yang.common.generator.SequenceGenerate; 11 | import cn.yang.common.util.BeanUtil; 12 | import cn.yang.common.util.MacUtils; 13 | import cn.yang.master.client.constant.MessageConstants; 14 | import cn.yang.master.client.exception.ConnectionException; 15 | import io.netty.channel.ChannelHandlerContext; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.util.StringUtils; 19 | 20 | import java.util.Arrays; 21 | 22 | /** 23 | * @author Cool-Coding 24 | * 2018/7/27 25 | */ 26 | public abstract class AbstractMasterCommandHandler implements ICommandHandler { 27 | 28 | /** logger */ 29 | private static final Logger LOGGER= LoggerFactory.getLogger(AbstractMasterCommandHandler.class); 30 | 31 | /** 32 | * 序号生成器 33 | */ 34 | private static final SequenceGenerate generator= BeanUtil.getBean(SequenceGenerate.class); 35 | 36 | /** 37 | *连接时得到ChannelHandlerContext,供其它命令使用,故使用静态成员变量 38 | */ 39 | private static ChannelHandlerContext ctx; 40 | 41 | @Override 42 | public void handle(ChannelHandlerContext ctx, Response response) throws Exception { 43 | final Enum command = response.getCommand(); 44 | if (command==null){ 45 | error(response, ExceptionMessageConstants.REQUIRED_COMMAND); 46 | ctx.channel().close(); 47 | throw new ResponseHandleException(ExceptionMessageConstants.REQUIRED_COMMAND); 48 | } 49 | 50 | handle0(ctx,response); 51 | } 52 | 53 | protected abstract void handle0(ChannelHandlerContext ctx, Response response) throws Exception; 54 | 55 | 56 | public Request buildRequest(Enum command, String puppetName, Object obj){ 57 | String mac= MacUtils.getMAC(); 58 | if (StringUtils.isEmpty(mac)){ 59 | return null; 60 | } 61 | 62 | Request request = new Request(); 63 | request.setId(Constants.MASTER + mac + generator.next()); 64 | request.setCommand(command); 65 | request.setPuppetName(puppetName); 66 | request.setValue(obj); 67 | return request; 68 | } 69 | 70 | protected void setChannelHandlerConext(ChannelHandlerContext ctx){ 71 | AbstractMasterCommandHandler.ctx=ctx; 72 | } 73 | 74 | protected void sendRequest(Request request) throws ConnectionException{ 75 | if (ctx!=null && ctx.channel()!=null && ctx.channel().isOpen()){ 76 | debug(request, MessageConstants.PREPARING_TO_FIRE); 77 | ctx.writeAndFlush(request); 78 | }else{ 79 | error(request, cn.yang.master.client.constant.ExceptionMessageConstants.CONNECTION_SERVER_FAILED); 80 | throw new ConnectionException(cn.yang.master.client.constant.ExceptionMessageConstants.CONNECTION_SERVER_FAILED); 81 | } 82 | } 83 | void error(Request request,String... message){ 84 | LOGGER.error("{}:{}",request, Arrays.toString(message)); 85 | } 86 | 87 | void error(Object object,String... message){ 88 | LOGGER.error("{}:{}",object.getClass().getCanonicalName(), Arrays.toString(message)); 89 | } 90 | 91 | void debug(Request request,String... message){ 92 | LOGGER.debug("{}:{}",request, Arrays.toString(message)); 93 | } 94 | 95 | void info(Request request,String... message){ 96 | LOGGER.info("{}:{}",request, Arrays.toString(message)); 97 | } 98 | 99 | void warn(Request request,String... message){ 100 | LOGGER.warn("{}:{}",request, Arrays.toString(message)); 101 | } 102 | 103 | void error(Response response,String message){ 104 | LOGGER.error("{}:{}",response,message); 105 | } 106 | 107 | void debug(Response response,String message){ 108 | LOGGER.debug("{}:{}",response,message); 109 | } 110 | 111 | void debug(String format,String message){ 112 | LOGGER.debug(format,message); 113 | } 114 | 115 | void info(Response response,String message){ 116 | LOGGER.info("{}:{}",response,message); 117 | } 118 | 119 | void warn(Response response,String message){ 120 | LOGGER.warn("{}:{}",response,message); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/impl/PuppetScreen.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui.impl; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.constant.Constants; 5 | import cn.yang.common.util.ImageUtils; 6 | import cn.yang.common.util.TaskExecutors; 7 | import cn.yang.master.client.exception.MasterClientException; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | import java.awt.image.BufferedImage; 14 | 15 | /** 16 | * @author cool-coding 17 | * 2018/7/27 18 | */ 19 | public class PuppetScreen extends AbstractDisplayPuppet implements ActionListener{ 20 | 21 | private QualitySlider qualitySlider; 22 | private BufferedImage image; 23 | 24 | 25 | public PuppetScreen(String puppetName){ 26 | super(puppetName); 27 | } 28 | 29 | 30 | @Override 31 | public void initMenu(JFrame jFrame) { 32 | JMenuBar menuBar=new JMenuBar(); 33 | JMenu setting=new JMenu("设置"); 34 | menuBar.add(setting); 35 | jFrame.setJMenuBar(menuBar); 36 | 37 | JMenuItem qualityItem=new JMenuItem("清晰度"); 38 | qualityItem.setActionCommand(Commands.QUALITY.name()); 39 | qualityItem.addActionListener(this); 40 | setting.add(qualityItem); 41 | } 42 | 43 | /** 44 | * 改变清晰度 45 | */ 46 | private void changeQuality(){ 47 | if (qualitySlider==null){ 48 | qualitySlider=new QualitySlider(); 49 | } 50 | 51 | SwingUtilities.invokeLater(()->{ 52 | qualitySlider.setVisible(true); 53 | }); 54 | } 55 | 56 | 57 | @Override 58 | public void actionPerformed(ActionEvent e) { 59 | switch (Commands.valueOf(e.getActionCommand())){ 60 | case QUALITY: 61 | changeQuality(); 62 | break; 63 | default: 64 | } 65 | } 66 | 67 | @Override 68 | protected void paint(Graphics g) { 69 | if(image!=null){ 70 | g.drawImage(image,0,0,null); 71 | } 72 | } 73 | 74 | @Override 75 | void generateImage(byte[] bytes) { 76 | this.image= ImageUtils.getImageFromByteArray(bytes); 77 | } 78 | 79 | //滑动条 80 | private class QualitySlider extends JDialog{ 81 | private static final long serialVersionUID = 5807019525801501790L; 82 | private int quality=Constants.SCREEN_QUALITY; 83 | private JSlider slider; 84 | private final static String OK_COMMAND_BUTTON="OK"; 85 | 86 | private QualitySlider(){ 87 | super(PuppetScreen.this.getjFrame()); 88 | Box sliderBox= new Box(BoxLayout.Y_AXIS); 89 | final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 90 | setLocation(screenSize.width/2,screenSize.height/2); 91 | 92 | slider = new JSlider(10,100,quality); 93 | //设置绘制刻度 94 | slider.setPaintTicks(true); 95 | //设置主、次刻度的间距 96 | slider.setMajorTickSpacing(20); 97 | slider.setMinorTickSpacing(5); 98 | //设置绘制刻度标签,默认绘制数值刻度标签 99 | slider.setPaintLabels(true); 100 | slider.addChangeListener((event)->{ 101 | //取出滑动条的值,并向傀儡端发送命令 102 | JSlider source = (JSlider) event.getSource(); 103 | quality=source.getValue(); 104 | }); 105 | 106 | JPanel buttonJpanel=new JPanel(); 107 | JButton button=new JButton("确定"); 108 | button.setActionCommand(OK_COMMAND_BUTTON); 109 | buttonJpanel.add(button); 110 | button.addActionListener((e)->{ 111 | if (OK_COMMAND_BUTTON.equals(e.getActionCommand())){ 112 | TaskExecutors.submit(()->{ 113 | try { 114 | masterDesktop.fireCommand(puppetName, Commands.QUALITY, quality); 115 | }catch (MasterClientException e2){ 116 | SwingUtilities.invokeLater(()->{ 117 | JOptionPane.showMessageDialog(PuppetScreen.this.getjFrame(),e2.getMessage()); 118 | }); 119 | } 120 | },0); 121 | 122 | } 123 | QualitySlider.this.setVisible(false);}); 124 | 125 | sliderBox.add(slider); 126 | sliderBox.add(buttonJpanel); 127 | 128 | add(sliderBox); 129 | pack(); 130 | setTitle("清晰度调节"); 131 | setModalityType(ModalityType.APPLICATION_MODAL); 132 | this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 36 | 37 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /puppet/puppet.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | file://$MODULE_DIR$/src/main/resources/puppet-beans.xml 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/commandhandler/AbstractPuppetCommandHandler.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.commandhandler; 2 | 3 | import cn.yang.common.command.Commands; 4 | import cn.yang.common.command.handler.ICommandHandler; 5 | import cn.yang.common.constant.Constants; 6 | import cn.yang.common.constant.ExceptionMessageConstants; 7 | import cn.yang.common.dto.Request; 8 | import cn.yang.common.dto.Response; 9 | import cn.yang.common.exception.ResponseHandleException; 10 | import cn.yang.common.generator.SequenceGenerate; 11 | import cn.yang.common.util.BeanUtil; 12 | import cn.yang.common.util.MacUtils; 13 | import cn.yang.common.util.PropertiesUtil; 14 | import cn.yang.puppet.client.constant.ConfigConstants; 15 | import cn.yang.puppet.client.ui.IReplay; 16 | import cn.yang.puppet.client.ui.MessageDialog; 17 | import io.netty.channel.ChannelHandlerContext; 18 | import org.slf4j.Logger; 19 | import org.slf4j.LoggerFactory; 20 | import org.springframework.util.StringUtils; 21 | 22 | import java.util.Arrays; 23 | 24 | /** 25 | * @author Cool-Coding 26 | * 2018/7/27 27 | */ 28 | public abstract class AbstractPuppetCommandHandler implements ICommandHandler { 29 | 30 | /** logger */ 31 | private static final Logger LOGGER= LoggerFactory.getLogger(AbstractPuppetCommandHandler.class); 32 | 33 | 34 | /** 35 | * 序号生成器 36 | */ 37 | private static final SequenceGenerate generator=BeanUtil.getBean(SequenceGenerate.class); 38 | 39 | /** 40 | * robot 41 | */ 42 | protected static IReplay REPLAY; 43 | 44 | /** 45 | * 标识:是否正处于被控制中 46 | */ 47 | private static volatile boolean isUnderControlled=false; 48 | 49 | /** 50 | * 傀儡名 51 | */ 52 | private static String puppetName; 53 | 54 | /** 55 | * 前一个截屏 56 | */ 57 | private static byte[] previousScreen; 58 | 59 | public AbstractPuppetCommandHandler() { 60 | String robot = PropertiesUtil.getString(ConfigConstants.CONFIG_FILE_PATH,ConfigConstants.ROBOT); 61 | REPLAY = BeanUtil.getBean(robot); 62 | } 63 | @Override 64 | public void handle(ChannelHandlerContext ctx, Response response) throws Exception { 65 | final Enum command = response.getCommand(); 66 | if (command == null){ 67 | LOGGER.error(ExceptionMessageConstants.REQUIRED_COMMAND); 68 | ctx.channel().close(); 69 | throw new ResponseHandleException(ExceptionMessageConstants.REQUIRED_COMMAND); 70 | } 71 | 72 | 73 | handle0(ctx,response); 74 | } 75 | 76 | protected abstract void handle0(ChannelHandlerContext ctx, Response response) throws Exception; 77 | 78 | public static Request buildConnectionRequest(int connectionCount){ 79 | return buildRequest(Commands.CONNECT,connectionCount); 80 | } 81 | 82 | public static Request buildRequest(Enum command, Object value){ 83 | String mac= MacUtils.getMAC(); 84 | if (StringUtils.isEmpty(mac)){ 85 | return null; 86 | } 87 | Request request = new Request(); 88 | request.setId(Constants.PUPPET + mac + generator.next()); 89 | request.setCommand(command); 90 | request.setPuppetName(puppetName); 91 | request.setValue(value); 92 | return request; 93 | } 94 | 95 | void error(Response response,String message){ 96 | LOGGER.error("{}:{}",response,message); 97 | } 98 | 99 | void error(Object object,String message){ 100 | LOGGER.error("{}:{}",object.getClass().getName(),message); 101 | } 102 | 103 | void debug(Response response,String... message){ 104 | LOGGER.debug("{}:{}",response,Arrays.toString(message)); 105 | } 106 | 107 | void debug(Request request,String... message){ 108 | LOGGER.debug("{}:{}",request,Arrays.toString(message)); 109 | } 110 | 111 | void info(Response response,String... message){ 112 | LOGGER.info("{}:{}",response,Arrays.toString(message)); 113 | } 114 | 115 | void warn(Response response,String message){ 116 | LOGGER.warn("{}:{}",response,message); 117 | } 118 | 119 | protected void startUnderControlled(){ 120 | AbstractPuppetCommandHandler.isUnderControlled=true; 121 | AbstractPuppetCommandHandler.previousScreen = null; 122 | } 123 | 124 | protected void stopUnderControlled(){ 125 | AbstractPuppetCommandHandler.isUnderControlled=false; 126 | AbstractPuppetCommandHandler.previousScreen=null; 127 | } 128 | 129 | protected static void setPreviousScreen(byte[] previousScreen){ 130 | AbstractPuppetCommandHandler.previousScreen = previousScreen; 131 | } 132 | 133 | protected static byte[] getPreviousScreen() { 134 | return previousScreen; 135 | } 136 | 137 | public static IReplay getReplay() { 138 | return REPLAY; 139 | } 140 | 141 | protected boolean isUnderControlled(){ 142 | return AbstractPuppetCommandHandler.isUnderControlled; 143 | } 144 | 145 | public static void setPuppetName(String puppetName) { 146 | AbstractPuppetCommandHandler.puppetName = puppetName; 147 | } 148 | 149 | public static String getPuppetName() { 150 | return puppetName; 151 | } 152 | 153 | /** 154 | * 显示消息对话框 155 | * @param title 156 | * @param message 157 | */ 158 | public static void popMessageDialog(String title,String message){ 159 | BeanUtil.getBean(MessageDialog.class,title).showMessage(message); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /common/src/main/java/cn/yang/common/InputEvent/MasterMouseEvent.java: -------------------------------------------------------------------------------- 1 | package cn.yang.common.InputEvent; 2 | 3 | import java.util.Arrays; 4 | 5 | import static java.awt.event.InputEvent.*; 6 | 7 | /** 8 | * @author Cool-Coding 9 | * 2018/7/26 10 | */ 11 | public class MasterMouseEvent { 12 | /** 13 | * 1.单击 14 | * 2.双击 15 | * 3.拖拽 16 | * 4.移动 17 | * 5.滚轮滚动 18 | * 6.按下 19 | * 7.释放 20 | */ 21 | private byte status; 22 | 23 | /** 24 | * 鼠标按键 25 | */ 26 | private MouseButton mouseButton; 27 | 28 | /** 29 | * 滚轮滚动的大小 30 | */ 31 | private int mouseWheel; 32 | 33 | /** 34 | * (x,y)坐标 35 | */ 36 | private int[] site ; 37 | 38 | public MasterMouseEvent(){ 39 | site=new int[2]; 40 | } 41 | 42 | public void buttonClicked(int mouseButton){ 43 | this.status=1; 44 | this.mouseButton= ConvertToMouseButton(mouseButton); 45 | } 46 | 47 | 48 | 49 | public void buttonDoubleClick(int mouseButton){ 50 | this.status=2; 51 | this.mouseButton= ConvertToMouseButton(mouseButton); 52 | } 53 | 54 | 55 | public void dragged(int mouseButton,int x,int y){ 56 | this.status=3; 57 | this.mouseButton= ConvertToMouseButton(mouseButton); 58 | setSite(x,y); 59 | } 60 | 61 | 62 | public void mouseMoved(int x,int y){ 63 | this.status=4; 64 | setSite(x,y); 65 | } 66 | 67 | 68 | public void mouseWheel(int mouseWheel){ 69 | this.status=5; 70 | this.mouseWheel=mouseWheel; 71 | } 72 | 73 | public void mousePressed(int mouseButton){ 74 | this.status=6; 75 | this.mouseButton= ConvertToMouseButton(mouseButton); 76 | } 77 | 78 | public void mouseReleased(int mouseButton){ 79 | this.status=7; 80 | this.mouseButton= ConvertToMouseButton(mouseButton); 81 | } 82 | 83 | private void setSite(int x,int y){ 84 | site[0]=x; 85 | site[1]=y; 86 | } 87 | 88 | public boolean isClicked(){ 89 | return this.status==1; 90 | } 91 | 92 | public boolean isDoubleClicked(){ 93 | return this.status==2; 94 | } 95 | 96 | public boolean isDragged(){ 97 | return this.status==3; 98 | } 99 | 100 | public boolean isMouseMoved(){ 101 | return this.status==4; 102 | } 103 | 104 | public boolean isMouseWheel(){ 105 | return this.status==5; 106 | } 107 | 108 | public boolean isMousePressed(){return this.status==6;}; 109 | public boolean isMouseReleased(){return this.status==7;}; 110 | 111 | public byte getStatus() { 112 | return status; 113 | } 114 | 115 | public void setStatus(byte status) { 116 | this.status = status; 117 | } 118 | 119 | public int getMouseButton() { 120 | if (mouseButton==null){ 121 | return 0; 122 | } 123 | 124 | switch (mouseButton){ 125 | case LEFT: 126 | return BUTTON1_DOWN_MASK; 127 | case MIDDLE: 128 | return BUTTON2_DOWN_MASK; 129 | case RIGHT: 130 | return BUTTON3_DOWN_MASK; 131 | case PRESSED: 132 | return java.awt.event.MouseEvent.MOUSE_PRESSED; 133 | case DRAGGED: 134 | return BUTTON1_DOWN_MASK; 135 | case RELEASED: 136 | return java.awt.event.MouseEvent.MOUSE_RELEASED; 137 | case WHEEL: 138 | return java.awt.event.MouseEvent.MOUSE_WHEEL; 139 | case CLICK: 140 | return java.awt.event.MouseEvent.MOUSE_CLICKED; 141 | default: 142 | throw new RuntimeException("wrong MouseButton value:"+mouseButton); 143 | } 144 | } 145 | 146 | public int getMouseWheel() { 147 | return mouseWheel; 148 | } 149 | 150 | public void setMouseWheel(int mouseWheel) { 151 | this.mouseWheel = mouseWheel; 152 | } 153 | 154 | public int[] getSite() { 155 | return site; 156 | } 157 | 158 | public void setSite(int[] site) { 159 | this.site = site; 160 | } 161 | 162 | private MouseButton ConvertToMouseButton(int mouseButton){ 163 | switch (mouseButton){ 164 | case java.awt.event.MouseEvent.BUTTON1: 165 | return MouseButton.LEFT; 166 | case java.awt.event.MouseEvent.BUTTON2: 167 | return MouseButton.MIDDLE; 168 | case java.awt.event.MouseEvent.BUTTON3: 169 | return MouseButton.RIGHT; 170 | case 0://调试发现为0时,表示拖动,鼠标移动也为零,但不调用此方法 171 | return MouseButton.DRAGGED; 172 | case java.awt.event.MouseEvent.MOUSE_WHEEL: 173 | return MouseButton.WHEEL; 174 | case java.awt.event.MouseEvent.MOUSE_CLICKED: 175 | return MouseButton.CLICK; 176 | case java.awt.event.MouseEvent.MOUSE_PRESSED: 177 | return MouseButton.PRESSED; 178 | case java.awt.event.MouseEvent.MOUSE_RELEASED: 179 | return MouseButton.RELEASED; 180 | default: 181 | throw new RuntimeException("not supported mouse button"); 182 | } 183 | } 184 | 185 | 186 | @Override 187 | public String toString() { 188 | return "MasterMouseEvent{" + 189 | "status=" + status + 190 | ", mouseButton=" + mouseButton + 191 | ", mouseWheel=" + mouseWheel + 192 | ", site=" + Arrays.toString(site) + 193 | '}'; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /puppet/src/main/java/cn/yang/puppet/client/robot/GoRobotReplay.java: -------------------------------------------------------------------------------- 1 | package cn.yang.puppet.client.robot; 2 | 3 | 4 | import cn.yang.common.InputEvent.MasterKeyEvent; 5 | import cn.yang.common.InputEvent.MasterMouseEvent; 6 | import cn.yang.common.util.BeanUtil; 7 | import cn.yang.common.util.ImageUtils; 8 | import cn.yang.puppet.client.PuppetStarter; 9 | import cn.yang.puppet.client.constant.PuppetDynamicSetting; 10 | import cn.yang.puppet.client.ui.IReplay; 11 | import com.google.protobuf.ByteString; 12 | import service.GoRobotGrpc; 13 | import service.GoRobotOuterClass; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Iterator; 17 | import java.util.List; 18 | 19 | /** 20 | * @author Cool-Coding 21 | * 2021/1/23 22 | */ 23 | @SuppressWarnings("ResultOfMethodCallIgnored") 24 | public class GoRobotReplay implements IReplay { 25 | 26 | /** 27 | * 因go与java的键盘对照不一致,直接使用java的robot操作按键 28 | */ 29 | protected static final IReplay defaultRobot = BeanUtil.getBean("javaRobot"); 30 | 31 | 32 | @Override 33 | public void keyPress(MasterKeyEvent keyEvent) { 34 | defaultRobot.keyPress(keyEvent); 35 | } 36 | 37 | @Override 38 | public void keyRelease(MasterKeyEvent keyEvent) { 39 | defaultRobot.keyRelease(keyEvent); 40 | } 41 | 42 | @Override 43 | public void mouseClick(MasterMouseEvent mouseEvent) { 44 | GoRobotOuterClass.MouseButton mouseButton = GoRobotOuterClass.MouseButton.newBuilder().setX(mouseEvent.getMouseButton()).build(); 45 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseClick(mouseButton); 46 | } 47 | 48 | 49 | @Override 50 | public void mouseWheel(MasterMouseEvent mouseEvent) { 51 | int mouseWheel = mouseEvent.getMouseWheel(); 52 | if (mouseWheel < 0) { 53 | GoRobotOuterClass.MouseScrolledRequest mouseScrolledRequest = GoRobotOuterClass.MouseScrolledRequest.newBuilder().setDistance(mouseWheel).build(); 54 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseScrolledUp(mouseScrolledRequest); 55 | } else { 56 | GoRobotOuterClass.MouseScrolledRequest mouseScrolledRequest = GoRobotOuterClass.MouseScrolledRequest.newBuilder().setDistance(mouseWheel * -1).build(); 57 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseScrolledDown(mouseScrolledRequest); 58 | } 59 | } 60 | 61 | @Override 62 | public void mousePress(MasterMouseEvent mouseEvent) { 63 | GoRobotOuterClass.MouseButton mouseButton = GoRobotOuterClass.MouseButton.newBuilder().setX(mouseEvent.getMouseButton()).build(); 64 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mousePressed(mouseButton); 65 | } 66 | 67 | @Override 68 | public void mouseRelease(MasterMouseEvent mouseEvent) { 69 | GoRobotOuterClass.MouseButton mouseButton = GoRobotOuterClass.MouseButton.newBuilder().setX(mouseEvent.getMouseButton()).build(); 70 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseReleased(mouseButton); 71 | } 72 | 73 | @Override 74 | public void mouseMove(int[] site) { 75 | GoRobotOuterClass.Point endPoint = GoRobotOuterClass.Point.newBuilder().setX(site[0]).setY(site[1]).build(); 76 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseMove(endPoint); 77 | } 78 | 79 | @Override 80 | public void mouseDoubleClick(MasterMouseEvent mouseEvent) { 81 | final int mouseButton1 = mouseEvent.getMouseButton(); 82 | GoRobotOuterClass.MouseButton mouseButton = GoRobotOuterClass.MouseButton.newBuilder().setX(mouseButton1).build(); 83 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseDoubleClick(mouseButton); 84 | } 85 | 86 | @Override 87 | public void mouseDragged(MasterMouseEvent mouseEvent, int[] site) { 88 | final int mouseButton1 = mouseEvent.getMouseButton(); 89 | 90 | GoRobotOuterClass.MouseButton mouseButton = GoRobotOuterClass.MouseButton.newBuilder().setX(mouseButton1).build(); 91 | GoRobotOuterClass.Point endPoint = GoRobotOuterClass.Point.newBuilder().setX(site[0]).setY(site[1]).build(); 92 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mousePressed(mouseButton); 93 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseMove(endPoint); 94 | GoRobotGrpc.newBlockingStub(PuppetStarter.goRobotChannel).mouseReleased(mouseButton); 95 | } 96 | 97 | @Override 98 | public byte[] getScreenSnapshot() { 99 | //截图(截取整个屏幕图片) 100 | GoRobotOuterClass.CaptureScreenRequest captureScreenRequest = 101 | GoRobotOuterClass.CaptureScreenRequest 102 | .newBuilder() 103 | .setWidth(0) 104 | .setHeight(0) 105 | .build(); 106 | Iterator captureScreenResponseIterator = GoRobotGrpc 107 | .newBlockingStub(PuppetStarter.goRobotChannel) 108 | .captureScreen(captureScreenRequest); 109 | 110 | List byteList = new ArrayList<>(); 111 | while (captureScreenResponseIterator.hasNext()) { 112 | ByteString bitmap = captureScreenResponseIterator.next().getBitmap(); 113 | byte[] bytes = bitmap.toByteArray(); 114 | for (byte b : bytes) { 115 | byteList.add(b); 116 | } 117 | } 118 | 119 | byte[] bitMapBits = new byte[byteList.size()]; 120 | for (int i = 0; i < byteList.size(); i++) { 121 | bitMapBits[i] = byteList.get(i); 122 | } 123 | return ImageUtils.compressedImageAndGetByteArray(ImageUtils.getImageFromByteArray(bitMapBits), PuppetDynamicSetting.quality / 100.0f); 124 | } 125 | } -------------------------------------------------------------------------------- /master/src/main/java/cn/yang/master/client/ui/listener/MouseListener.java: -------------------------------------------------------------------------------- 1 | package cn.yang.master.client.ui.listener; 2 | 3 | import cn.yang.common.InputEvent.MasterMouseEvent; 4 | import cn.yang.common.command.Commands; 5 | import cn.yang.common.util.BeanUtil; 6 | import cn.yang.common.util.PropertiesUtil; 7 | import cn.yang.common.util.TaskExecutors; 8 | import cn.yang.master.client.constant.ConfigConstants; 9 | import cn.yang.master.client.exception.MasterClientException; 10 | import cn.yang.master.client.netty.MasterNettyClient; 11 | import cn.yang.master.client.ui.IDisplayPuppet; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | import javax.swing.*; 16 | import java.awt.*; 17 | import java.awt.event.MouseAdapter; 18 | import java.awt.event.MouseEvent; 19 | import java.awt.event.MouseWheelEvent; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | 22 | /** 23 | * @author Cool-Coding 24 | * 2018/7/26 25 | */ 26 | public class MouseListener extends MouseAdapter { 27 | private static final Logger LOGGER = LoggerFactory.getLogger(MouseListener.class); 28 | /** 29 | * 记录点击次数 30 | */ 31 | private AtomicInteger clickNum = new AtomicInteger(0); 32 | /** 33 | * 是否已经 34 | */ 35 | private volatile boolean isDoubleClicked; 36 | /** 37 | * 记录上次单击的鼠标键位 38 | */ 39 | private int preButton = -1; 40 | private MasterNettyClient masterClient; 41 | private IDisplayPuppet puppetScreen; 42 | 43 | private Point lastPoint = new Point(); 44 | 45 | public MouseListener(IDisplayPuppet puppetScreen) { 46 | this.puppetScreen = puppetScreen; 47 | masterClient = BeanUtil.getBean(MasterNettyClient.class, "masterClient"); 48 | } 49 | 50 | @Override 51 | public void mouseClicked(MouseEvent e) { 52 | //每次单击都将此标识示false,防止双击后的单击被认为是双击 53 | isDoubleClicked=false; 54 | 55 | //点击数加1如果等于2,说明单击了两次,并且两次单击了相同的键,则是双击 56 | if (clickNum.incrementAndGet()==2 && isTheSameButton(e)) { 57 | isDoubleClicked = true; 58 | preButton = -1; 59 | this.mouseDoubleClicked(e); 60 | return; 61 | } 62 | 63 | //定义定时器 64 | TaskExecutors.submit(()->{ 65 | if(isDoubleClicked){//如果双击事件已经执行,那么直接取消单击执行 66 | clickNum.set(0); 67 | isDoubleClicked=false; 68 | return; 69 | } 70 | if (clickNum.getAndDecrement() == 1) {//定时器等待0.2秒后,双击事件仍未发生,执行单击事件 71 | preButton = -1; 72 | mouseSingleClicked(e); 73 | } 74 | }, PropertiesUtil.getInt(ConfigConstants.CONFIG_FILE_PATH,ConfigConstants.MOUSE_DOUBLE_CHECK_DELAY,500)); 75 | 76 | preButton=e.getButton(); 77 | } 78 | 79 | //此次单击的键与上次是否为同一个键 80 | private boolean isTheSameButton(MouseEvent e) { 81 | return preButton != -1 && e.getButton() == preButton; 82 | } 83 | 84 | @Override 85 | public void mousePressed(MouseEvent e) { 86 | MoveMouseIfNecessary(e); 87 | 88 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 89 | mouseEvent.mousePressed(e.getButton()); 90 | LOGGER.debug("mouse pressed:{}", mouseEvent); 91 | sendMosueEvent(mouseEvent); 92 | } 93 | 94 | /** 95 | * 鼠标单击事件 96 | * 97 | * @param e 事件源参数 98 | */ 99 | public void mouseSingleClicked(MouseEvent e) { 100 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 101 | mouseEvent.buttonClicked(e.getButton()); 102 | mouseEvent.setSite(new int[]{e.getX(), e.getY()}); 103 | LOGGER.debug("mouse clicked:{}", mouseEvent); 104 | sendMosueEvent(mouseEvent); 105 | } 106 | 107 | /** 108 | * 鼠标双击事件 109 | * 110 | * @param e 事件源参数 111 | */ 112 | private void mouseDoubleClicked(MouseEvent e) { 113 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 114 | mouseEvent.buttonDoubleClick(e.getButton()); 115 | LOGGER.debug("mouse double clicked:{}", mouseEvent); 116 | sendMosueEvent(mouseEvent); 117 | } 118 | 119 | @Override 120 | public void mouseReleased(MouseEvent e) { 121 | if (e.getComponent().getCursor().getType() == Cursor.HAND_CURSOR) { 122 | e.getComponent().setCursor(Cursor.getDefaultCursor()); 123 | } 124 | 125 | 126 | MoveMouseIfNecessary(e); 127 | 128 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 129 | 130 | mouseEvent.mouseReleased(e.getButton()); 131 | LOGGER.debug("mouse released:{}", mouseEvent); 132 | sendMosueEvent(mouseEvent); 133 | } 134 | 135 | @Override 136 | public void mouseMoved(MouseEvent e) { 137 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 138 | mouseEvent.mouseMoved(e.getX(), e.getY()); 139 | LOGGER.debug("mouse moved:{}", mouseEvent); 140 | sendMosueEvent(mouseEvent); 141 | } 142 | 143 | private void MoveMouseIfNecessary(MouseEvent e) { 144 | if (Math.abs(lastPoint.x - e.getX()) > 2 || Math.abs(lastPoint.y - e.getY()) > 2) { 145 | lastPoint.x = e.getX(); 146 | lastPoint.y = e.getY(); 147 | //先移动 148 | mouseMoved(e); 149 | } 150 | } 151 | 152 | private void sendMosueEvent(MasterMouseEvent mouseEvent) { 153 | try { 154 | masterClient.fireCommand(puppetScreen.getPuppetName(), Commands.MOUSE, mouseEvent); 155 | } catch (MasterClientException e) { 156 | JOptionPane.showMessageDialog(null, e.getMessage()); 157 | } 158 | } 159 | 160 | @Override 161 | public void mouseWheelMoved(MouseWheelEvent e) { 162 | final MasterMouseEvent mouseEvent = new MasterMouseEvent(); 163 | int count = e.getWheelRotation(); 164 | mouseEvent.mouseWheel(count); 165 | LOGGER.debug("mouse wheel:{}", count); 166 | sendMosueEvent(mouseEvent); 167 | } 168 | 169 | static class Point { 170 | int x = 0; 171 | int y = 0; 172 | } 173 | } 174 | --------------------------------------------------------------------------------