?
class="MsoTitle">?
--by?Shadow Walker
上一篇博文已经分析了Java实现远程监控的思路,本篇博文主要分享主要部分的代码实现、
?
用imago.IO封装桌面截图图片发送
?
robot = new Robot(); while (flag) { // 抓去一张屏幕大小的图片 BufferedImage bgImg = robot.createScreenCapture(rect); // 将抓取的图片转换为byte数组 ByteArrayOutputStream baous = new ByteArrayOutputStream(); ImageIO.write(bgImg, "jpeg", baous); byte[] bytes = baous.toByteArray(); dous.writeInt(bytes.length);// 写入发送图片信息的长度 dous.write(bytes);// 写入组成图片的字节数据 dous.flush(); Thread.sleep(150); }
?
被控端将所有的鼠标键盘的输入转化为Robot对象的事件回放
?
/** * 处理控制端发送来的事件对象,调用Robot对象来执行相应的操作 * * @param event * 网络收到的控制端事件对象 */ private void handleEvent(InputEvent event) { MouseEvent mouseEvent = null; MouseWheelEvent mouseWheelEvent = null; KeyEvent keyEvent = null; int mouseButtonMask = -1; switch (event.getID()) { case MouseEvent.MOUSE_MOVED:// 鼠标移动事件 mouseEvent = (MouseEvent) event; robot.mouseMove((int) (mouseEvent.getX()), (int) (mouseEvent.getY())); break; case MouseEvent.MOUSE_PRESSED:// 鼠标按键按下事件 mouseEvent = (MouseEvent) event; mouseButtonMask = getButtonMask(mouseEvent.getButton());// 获取按键标志 robot.mousePress(mouseButtonMask); break; case MouseEvent.MOUSE_RELEASED:// 鼠标按键释放事件 mouseEvent = (MouseEvent) event; mouseButtonMask = getButtonMask(mouseEvent.getButton());// 获取按键标志 robot.mouseRelease(mouseButtonMask); break; case MouseEvent.MOUSE_WHEEL:// 鼠标中键滚动事件 mouseWheelEvent = (MouseWheelEvent) event; robot.mouseWheel(mouseWheelEvent.getWheelRotation()); break; case MouseEvent.MOUSE_DRAGGED:// 鼠标拖拽事件 mouseEvent = (MouseEvent) event; robot.mouseMove((int) (mouseEvent.getX()), (int) (mouseEvent.getY())); break; case KeyEvent.KEY_PRESSED:// 键盘按键按下事件 keyEvent = (KeyEvent) event; robot.keyPress(keyEvent.getKeyCode()); break; case KeyEvent.KEY_RELEASED:// 键盘按键释放事件 keyEvent = (KeyEvent) event; robot.keyRelease(keyEvent.getKeyCode()); break; default: LogTool.INFO("unknown event" + event.getID()); break; } } /** * 根据发送的Mouse事件对象,转变为通用的Mouse按键代码 * * @param button * @return */ private int getButtonMask(int button) { if (button == MouseEvent.BUTTON1) { return InputEvent.BUTTON1_MASK; } if (button == MouseEvent.BUTTON2) { return InputEvent.BUTTON2_MASK; } if (button == MouseEvent.BUTTON3) { return InputEvent.BUTTON3_MASK; } return -1; }
?
?
控制方与被控方之间传输事件
?
/** * 将监听的动作对象写入输入流中 * * @param event * 监听的事件对象 */ public void sendAction(InputEvent event) { try { oous.writeObject(event); } catch (IOException e) { LogTool.ERROR("控制端动作转信息类中写入异常:" + e.getMessage().toString()); } }
?
?
一些小方面的优化:
?
使用Image.IO和JPEGImageEncoder封装jpg图片发送截图图片的时间差异。
?
/**log output 2013-10-18 15:54:00 ?Image.IO cost time141 bgImg size:114040 2013-10-18 15:54:00 ?JPEGImageEncoder cost time16 bgImg size:114258 */
?
so,果断采用JPEGImageEncoder封装发送图片
?
/** * 将背景图片压缩成JPEG并发送到控制端 * * @param rec * @return * @throws IOException */ private byte[] createImage(Rectangle rec) throws IOException { BufferedImage bfImage = robot.createScreenCapture(rec); ByteArrayOutputStream baous = new ByteArrayOutputStream(); JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(baous); encoder.encode(bfImage); return baous.toByteArray(); }
?
?
添加第三方监视方,数量不限,可以实现同步的监视,但是不能控制。
因而第一台与服务器连接等客户机即为控制方,以后的客户机均为监视方,显示被控端界面的信息。
?
/** * 在指定端口上启动一个服务器 * * @param port * :服务器开端口 */ public void setUpServer(int port) { try { // 1.建立绑定在指定端口上的服务器对象 @SuppressWarnings("resource") ServerSocket server = new ServerSocket(port); LogTool.INFO("服务器创建成功" + port); // 2.当有客户机连接时,等待方法就会返回,返回一个代表与客户端连接的对象 while (true) { if (state == true) {// 第一个客户机连接为控制端,以后连接的均为监视端 // 让服务器处于循环待机状态 Socket client = server.accept();// 可能会发生堵塞 LogTool.INFO("Incoming" + client.getRemoteSocketAddress().toString()); LogTool.INFO("处理线程已启动,正处理登录操作! -->控制端"); RemoteScreenThread rct = new RemoteScreenThread(client); rct.start(); // 开启被控端动作还原线程 RemoteRobotThread rrt = new RemoteRobotThread(client); rrt.start(); state = false; } else {// 如若服务器已经连接上,这客户机为监视端 // 让服务器处于循环待机状态 Socket client = server.accept();// 可能会发生堵塞 LogTool.INFO("Incoming" + client.getRemoteSocketAddress().toString()); LogTool.INFO("处理线程已启动,正处理登录操作! ———>监视端"); RemoteScreenThread rct = new RemoteScreenThread(client); MonitorTools.addClient(rct); LogTool.INFO("添加监视端对象成功"); //将截图信息群发到监视端里面 MonitorTools.castScreen(); // RemoteScreenThread rct = new RemoteScreenThread(client); // MonitorTools.addClient(client); } } } catch (Exception ef) { LogTool.ERROR("被控端开启端口监听器中出错:" + ef.getMessage()); } } }
?
?
?
后台写一个监视辅助类,主要存储监视方对象,并对监视方发送被控端截图信息。
由于该类不用创建对象,故定义为静态类,所有方法均为静态方法。
?
/** * 监控工具类,将所有的的客户端线程添加到队列中 * @author YangKang * */ public class MonitorTools { private static ArrayList<RemoteScreenThread> rctlist = new ArrayList<RemoteScreenThread>();//连接的监视端队列 private MonitorTools(){}//不需要创建类对象,构造器则私有 /** * 将一个客户机对应的处理线程加入到队列中 * @param st 处理线程对象 */ public static void addClient(RemoteScreenThread rct) { try { //通知大家一声,有人上线了 // castMsg(cct.getOwnerUser(),"我上线了,目前在线人数:"+stlist.size()); rctlist.add(rct); } catch (Exception e) { LogTool.ERROR("MonitorTools类中线程对象添加到队列失败"+e.getMessage().toString()); } } /** * 将客户端发送过来的消息转发送给队列中的其他客户端处理对象 * @param sender 发送者用户对象 * @param msg 要发送的消息内容 * @throws Exception IO异常 */ public static void castScreen() { for (int i = 0; i < rctlist.size(); i++) { rctlist.get(i).start();; } } }
?
?
?
源代码打包上传地址:http://pan.baidu.com/s/1cMqOL
?
?
?
希望大家多多支持和指正。