远程桌面控制(Spring+Netty+Swing) (2)

Invocation类是一个基类,请求类(Request)和响应类(Response)在此基础之上扩展。
Invocation类中有一个成员变量是命令command,我们来看一下:

命令类 Commands /** * @author cool-coding * 2018/7/27 * 命令 */ public enum Commands{ /** * 控制端或傀儡端连接服务器时的命令 */ CONNECT, /** * 控制命令 * 1.主人向服务器发送控制请求 * 2.服务器将控制命令发给傀儡 * 3.傀儡收到控制命令,将向服务器发送截屏 */ CONTROL, /** * 傀儡发送心跳给服务器 */ HEARTBEAT, /** * 傀儡发送屏幕截图命令 */ SCREEN, /** * 控制端发送键盘事件 */ KEYBOARD, /** * 控制端发送鼠标事件 */ MOUSE, /** * 断开控制傀儡 */ TERMINATE, /** * 清晰度 */ QUALITY }

目前一共有8个命令,有的命令是M和P共用,有的是一方单用。

命令处理接口 ICommandHandler public interface ICommandHandler<T> { /** * * @param ctx 当前channel处理器上下文 * @param inbound channel输入对象 * @throws Exception 异常 */ void handle(ChannelHandlerContext ctx,T inbound) throws Exception; }

ICommandHandler接口是所有命令处理类的父接口,Netty ChannelHandler在处理请求时,根据不同的命令,寻找对应的处理类。

一些设计想法 心跳与屏幕截图

心跳和屏幕截图都是定时向服务器发送,所以在设计时这两者同时只有一个活动即可。即发送心跳时不发送屏幕截图,发送屏幕截图时不发送心跳,控制结束后,继续发送心跳。这两者之间的控制由Puppet模块中ConnectCommandHandler类中的HeartBeatAndScreenSnapShotTaskManagement内部类控制。

命令分层

通过对用例和流程的分析,发现命令出现的频率比较高,于是考虑将命令处理单独独立出来,采取动态加载的方式,使其与ChannelHandler解耦,使用后期扩展,而且当命令很多时,不需要一次都加载,只是在使用时按需加载,减少JVM加载类的字节码量,此处参考了SPI思想。而添加命令,势必会修改界面,我使用模板模式,预留出菜单,界面体,界面属性设置等,修改时只需继续相关类并修改,然后在spring配置文件进行配置即可。

序列号和Puppet名称生成器

请求和响应类中都有ID属性,其中一部分是通过序列号生成器生成的,所以提供了SequenceGenerate接口和一个简单的实现类SimpleSequenceGenerator。同理还有当傀儡连接服务器时,服务器生成唯一的傀儡名,也提供了一个简单的实现类SimplePuppetNameGenerator。

图像处理

图像的数据相对于纯命令来说大了许多,所以需要想办法减少图像传输的数据,大致有两种方式:

选择合适的图片格式,并进行压缩:我这里选择了jpg格式,并使用Google Thumbnailator工具进行等宽高压缩,因为jpg具有较高的压缩比,但是代价是压缩后图像的质量不是太理想。

只传输变化的图像:很多时候图像变化的部分并不太多,可以只传输变化的区域,传输到控制端后,控制端只绘制变化的区域。
(1). 像素级别: 我的思路是在傀儡端保持前一次传输时的截屏,和本次截屏图像进行像素级的比较,将不同的像素保存到一个对象数组中,记录像素的位置和像素值,传输到控制端后,根据像素位置和要替换的像素进行绘制
(2). 区域级别:只记录变化图像的开始点(左上角)和结束点(右下角),然后绘制以这两个点框定的矩形式区域。
我尝试了这两种方式,自己对于Swing绘制图像这块不太精通,没有达到很好的效果,最后采取了压缩图像的方式。若有更好的方式,可以通过继承Puppet模块中抽象类AbstractRobotReplay,实现屏幕截屏方法byte[] getScreenSnapshot(),然后继承Master模块中抽像类AbstractDisplayPuppet实现其中的paint方法(也可以继承现有的实现类PuppetScreen,覆盖相应的方法),然后将自定义的类在spring配置文件中配置,替换掉现在的实现类即可。

未解决问题

快速按键的情况、双击时响应的比较慢。传输命令需要时间,所以快速按键时命令产生滞后现象,而傀儡端图像传输到控制端后,Swing是单线程处理AWT事件(鼠标、键盘、绘图等),若此时仍在按键,则会阻塞,等到按键结束之后,再进行图像的绘制。

一点心得

需求分析很重要,分析需求中各对象的属性和行为,以及对象之间的关系,这是后面功能、领域模型、静态/动态模型分析的基础。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjppd.html