欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-01 11:52:23
...

 

“指尖上的遥控”项目——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

 

 

 

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