`
什么世道
  • 浏览: 219264 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

Java实现远程监控之二:代码实现及其优化

阅读更多

 

“指尖上的遥控”项目——Java远程监控

 

--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

 

 

 

希望大家多多支持和指正。

1
0
分享到:
评论
2 楼 什么世道 2013-10-31  
784222511 写道
那个密码框有什么用?

监控前是为了安全起见设置一个密码的,但是发博客的时候还没有加上去.
1 楼 784222511 2013-10-29  
那个密码框有什么用?

相关推荐

Global site tag (gtag.js) - Google Analytics