内容简介:最近使用node实现了一个远程桌面监控的应用,分为服务端和客户端,客户端可以实时监控服务端的桌面,并且可以通过鼠标和键盘来控制服务端的桌面。这里因为我是用的同一台电脑,所以监控画面是这样的,当然使用两台电脑一个跑其实这个应用的功能主要分为两部分,一是实现监控,即在客户端可以看到服务端的桌面,这部分功能是通过定时截图来实现的,比如服务端一秒截几次图,然后通过
最近使用node实现了一个远程桌面监控的应用,分为服务端和客户端,客户端可以实时监控服务端的桌面,并且可以通过鼠标和键盘来控制服务端的桌面。
这里因为我是用的同一台电脑,所以监控画面是这样的,当然使用两台电脑一个跑 客户端 ,一个跑 服务端 才有意义。
原理
其实这个应用的功能主要分为两部分,一是实现监控,即在客户端可以看到服务端的桌面,这部分功能是通过定时截图来实现的,比如服务端一秒截几次图,然后通过 socketio 发送到客户端,客户端通过改变img的src来实现一帧帧的显示最新的图片,这样就能看到动态的桌面了。监控就是这样实现的。
另一个功能是控制,即客户端对监控画面的操作,包括鼠标和键盘的操作都可以在服务端的桌面真正的生效,这部分功能的实现是在electron的应用中监听了所有的鼠标和键盘事件,比如keydown、keyup、keypress,mousedown、mouseup、mousemove、click等,然后通过socketio把事件传递到服务端,服务端通过 robot-js 来执行不同的事件,这样就能使得客户端的事件在服务端触发了。
实现
原理讲完,我们来具体实现一下( 源码链接在这 )。
实现socket通信
首先,服务端和客户端分别引入 socket.io 和 socket.io-client , 分别初始化
服务端:
const app = new Koa();
const server = http.createServer(app.callback());
createSocketIO(server);
app.use((ctx): void => {
ctx.body = 'please connect use socket';
});
server.listen(port, (): void => {
console.log('server started at http://localhost:' + port);
});
复制代码
//createSocketIO
const io = socketIO(server, {
pingInterval: 10000,
pingTimeout: 5000,
cookie: false
});
io.on('connect', (socket): void => {
socket.emit('msg', 'connected');
}
复制代码
客户端:
var socket = this.socket = io('http://' + this.ip + ':3000')
socket.on('msg', (msg) => {
console.log(msg)
})
socket.on('error', (err) => {
alert('出错了' + err)
})
复制代码
这样,服务端和客户端就通过socketio建立了链接。
实现桌面监控
之后我们首先要在服务端来截图,使用 screenshot-desktop 这个包
const screenshot = require('screenshot-desktop')
const SCREENSHOT_INTERVAL = 500;
export const createScreenshot = (): Promise<[string, Buffer]> => {
return screenshot({format: 'png'}).then((img): [string, Buffer] => {
return [ img.toString('base64'), img];
}).catch((err): {} => {
console.log('截图失败', err);
return err;
})
}
export const startScreenshotTimer = (callback): {} => {
return setInterval((): void => {
createScreenshot().then(([imgStr, img]): void => {
callback(['data:image/png;base64,' + imgStr, img]);
})
}, SCREENSHOT_INTERVAL)
}
复制代码
然后通过socketio的emit来传到客户端:
startScreenshotTimer(([imgStr, img]): void => {
io.sockets.emit('screenshot', imgStr);
});
复制代码
客户端收到图片后,设置到img的src上(这里是base64的图片url):
<img
class="screenshot"
:src="screenshot"
/>
复制代码
data () {
return {
screenshot: ''
}
}
复制代码
socket.on('screenshot', (data) => {
this.screenshot = data
})
复制代码
其实这样就已经实现了桌面监控了,有兴趣的同学可以照着这个思路实现看看,并不是很麻烦。
当然这样的方案是有问题的,因为我们需要知道服务端桌面尺寸的大小,然后根据这个来调整客户端显示的图片尺寸。
实现这个细节是使用的 get-pixels 这个库,可以读取本地图片文件的宽度高度等信息,所以我先把图片写入本地,然后又读取出来,这样获取到的屏幕尺寸。
interface ScreenSize {
width: number;
height: number;
}
function getScreenSize(img): Promise<ScreenSize> {
const imgPath = path.resolve(process.cwd(), './tmp.png');
fs.writeFileSync(imgPath, img);
return new Promise((resolve): void => {
getPixels(imgPath, function(err, pixels): void {
if(err) {
console.log("Bad image path")
return
}
resolve({
width: pixels.shape[0],
height: pixels.shape[1]
});
});
})
}
复制代码
然后通过socektio传递给客户端
getScreenSize(img).then(({ width, height}) => {
io.sockets.emit('screensize', {
width,
height
})
});
复制代码
客户端收到之后调整图片大小就可以了
<img
class="screenshot"
:src="screenshot"
:style="screenshotStyle"
/>
复制代码
data () {
return {
screenshot: '',
screenshotStyle: '',
}
}
复制代码
socket.on('screensize', (screensize) => {
this.screenshotStyle = {'width': screensize.width + 'px', 'height': screensize.height + 'px'}
})
复制代码
至此已经实现了桌面监控,并且图片尺寸和服务端屏幕的尺寸是一致的。
这里还有一个细节,就是获取到的图片大小是物理像素,而客户端设置的px是设备无关像素,也就是要除以dpr才是px的值。这里需要获取dpr,因为目前只是在mac下用,所以直接除以2了。
实现远程控制
代码写到这里,客户端的electron应用中已经可以实时显示服务端的桌面了。(当然像输入ip的弹框,以及electron-vue和typescript等和主要逻辑无关的细节就不展开了。)
接下来我们要实现远程控制,也就是监听事件,传递事件,执行事件这几部分。
首先我们定义一下传递的事件的格式:
interface MouseEvent {
type: string;
buttonType: string;
x: number;
y: number;
}
interface KeyboardEvent {
type: string;
keyCode: number;
keyName: string;
}
复制代码
鼠标事件MouseEvent,type为鼠标事件的类型,具体的值包括mousedown、mouseup、mousemove、click、dblclick,buttonType指的是鼠标的左键还是右键,值为 left 或 right,x和y是具体的坐标。
键盘事件KeyboardEvent,type为键盘事件的类型,具体的值包括keydown、keyup、keypress,keyCode为键盘码,keyName为键的名字。
接下来我们要在客户端监听事件:
<img
class="screenshot"
:src="screenshot"
:style="screenshotStyle"
@mousedown="handleMouseEvent"
@mousemove="handleMouseEvent"
@mouseup="handleMouseEvent"
@click="handleMouseEvent"
@dblclick="handleMouseEvent"
/>
复制代码
window.onkeypress = window.onkeyup = window.onkeydown = this.handleKeyboardEvent 复制代码
通过socekt把事件传递到服务端
handleKeyboardEvent (e) {
this.socket && this.socket.emit('userevent', {
type: 'keyboard',
event: {
type: e.type,
keyName: e.key,
keyCode: e.keyCode
}
})
},
handleMouseEvent (e) {
this.socket && this.socket.emit('userevent', {
type: 'mouse',
event: {
type: e.type,
buttonType: e.buttons === 2 ? 'right' : 'left',
x: e.clientX,
y: e.clientY
}
})
},
复制代码
然后在服务端把事件取出来执行,执行事件使用的是 robot-js :
const { Mouse, Point, Keyboard } = require('robot-js');
interface MouseEvent {
type: string;
buttonType: string;
x: number;
y: number;
}
interface KeyboardEvent {
type: string;
keyCode: number;
keyName: string;
}
export default class EventExecuter {
public mouse;
public keyboard;
public constructor(){
this.mouse = new Mouse();
this.keyboard = new Keyboard();
}
public executeKeyboardEvent(event: KeyboardEvent): void {
switch(event.type) {
case 'keydown':
this.keyboard.press(event.keyCode);
break;
case 'keyup':
this.keyboard.release(event.keyCode);
break;
case 'keypress':
this.keyboard.click(event.keyCode);
break;
default: break;
}
}
public executeMouseEvent(event): void {
Mouse.setPos(new Point(event.x, event.y));
const button = event.buttonType === 'left' ? 0 : 2
switch(event.type) {
case 'mousedown':
this.mouse.press(button);
break;
case 'mousemove':
break;
case 'mouseup':
this.mouse.release(button);
break;
case 'click':
this.mouse.click(button);
break;
case 'dblclick':
this.mouse.click(button);
this.mouse.click(button);
break;
default: break;
}
}
public exectue(eventInfo): void {
console.log(eventInfo);
switch (eventInfo.type) {
case 'keyboard':
this.executeKeyboardEvent(eventInfo.event);
break;
case 'mouse':
this.executeMouseEvent(eventInfo.event);
break;
default: break;
}
}
}
复制代码
至此,桌面监控和远程控制的客户端还有服务端的部分,以及两端的通信都已经实现了。思路其实并不麻烦,但细节还是很多的。有兴趣的同学可以把代码下下来跑跑试试,或者按着这个思路自己实现一遍,还是挺好玩的。
以上所述就是小编给大家介绍的《nodejs实现远程桌面监控》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Building Web Reputation Systems
Randy Farmer、Bryce Glass / Yahoo Press / 2010 / GBP 31.99
What do Amazon's product reviews, eBay's feedback score system, Slashdot's Karma System, and Xbox Live's Achievements have in common? They're all examples of successful reputation systems that enable ......一起来看看 《Building Web Reputation Systems》 这本书的介绍吧!