“指尖上的遥控”项目——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
希望大家多多支持和指正。