使用noVnc远程控制桌面后自动打开应用程序

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 使用noVnc远程控制桌面后自动打开应用程序

项目描述



该项目的需求是当我们使用noVnc远程连接另一台电脑后,能打开一款名叫tecplot的软件。


该项目时序图



image.png

项目实现思路



1.成功连接noVnc后,修改noVnc里面的源代码。采用websocket进行客户端和服务器之间的通讯

2.在websocket serve端采用父子进程的方式打开应用程序

3.使用log4j进行项目日志的管理

4.用pm2来启动node服务端项目


客户端实现



noVnc的使用和安装大家可以看一下这篇博客 All in Web | 基于web的远程桌面-noVNC - 知乎 (zhihu.com)

修改noVnc.html源码,在连接成功之后执行websocket的连接,发送消息


const CMD_CODE = {
    "OPEN":0,
    "CLOSE":1,
}
function NoVncClient(IP,urlFile) {
    this.pid = null;
    this.wsClient = null;
    this.IP = IP;
    this.fileUrl = urlFile;
    this.connect = connect;
    this.onMessage = receiveMsg;
    this.disConnect = disConnect
}
function connect() {
    console.log('this con', this)
    this.wsClient = new WebSocket(`ws://${this.IP}:8080`);
    const url = this.fileUrl;
    this.wsClient.onopen = function () {
        console.log(this.readyState,'连接成功')
        const sendMsg = {
            cmd:CMD_CODE.OPEN,
            url:url,
        }
        console.log('sendMsg',sendMsg)
        this.send(JSON.stringify(sendMsg))
    }
    this.wsClient.addEventListener('error', function (event) {
        console.log('WebSocket error: ', event);
    });
}
function receiveMsg() {
    const that = this;
    this.wsClient.onmessage = function (msg) {
        const receiveFromServerMsg = JSON.parse(msg.data)
        // UI.showStatus(receiveFromServerMsg, 'normal');
        console.log('remote msg',receiveFromServerMsg)
        that.pid = receiveFromServerMsg.pid
    }
}
function disConnect() {
    console.log(this.pid,this,'this dis')
    const closeMsg = {
        pid:this.pid,
        cmd:CMD_CODE.CLOSE,
        url:this.fileUrl
    }
    console.log('close',closeMsg)
    // UI.showStatus(closeMsg, 'normal');
    this.wsClient.send(JSON.stringify(closeMsg))
}
function getUrlParamsByName(name) {
    let reg = new RegExp(`(?<=\b${name}=)[^&]*`),
        str = location.search || '',
        target = str.match(reg);
    if(target) {
        return target[0]
    }
}
const IP=document.domain;
const noClient = new NoVncClient(IP,getUrlParamsByName('path'));
noClient.connect()
noClient.onMessage()
window.onbeforeunload = function(){
    noClient.disConnect()
}


服务端实现



创建websocket连接


const ws =new WebSocketServer({
    port:PORT
})


收到客户端的消息


ws.on('connection', function(ws){
    recLog.send({msg:'websocket connect success'})
    ws.on('message',function(message){
        console.log('父进程收到客户端的消息',JSON.parse(message))
        receiveMsgFromClient(message);
    })
    setInterval(() =>transmitMsg(ws),10000)
})


向客户端发送消息


// 这里的ws是connection回调中的ws,而不是创建的ws实例.
// 不同客户端的连接对应一个不同的ws
ws.send(JSON.stringify(msg))


采用子进程启动应用程序


const parentInstance = ()=> {
    const parent = spawn("node", ["child.js"], {
        cwd: path.resolve(__dirname, './child'),
        stdio: [0, 1, 2, "ipc"]
    })
    return parent;
}
- options
    - cwd:Current working directory of the child process.子进程当前的工作目录
    - stdio: ['inherit', 'inherit', 'inherit','ipc']使用ipc的方法进行通信


父进程转发消息给客户端和子进程


const transmitMsg = (ws)=>{
    const parent = parentInstance()
    if(messageQue.length){
        const msg = messageQue.shift()
        console.log('父进程发送给子进程消息')
        parent.send(msg)
    }
    parent.on("message",(data)=>{
        const {code} = data;
        console.log('父进程收到子进程消息')
        const recLog = log4jInstance()
        const sendVal = JSON.stringify(data);
        switch (code) {
            case PROCESS_CODE.RUNNING:
                childProcessNum++;
                recLog.send({msg:sendVal})
                ws.send(sendVal)
                break;
            case PROCESS_CODE.EXIT:
                childProcessNum--;
                ws.send(sendVal)
                recLog.send({msg:sendVal})
                break;
            case PROCESS_CODE.ERROR:
                ws.send(sendVal)
                recLog.send({msg:sendVal})
                break;
            default:
                break;
        }
    })
}


子进程接受,发送消息


process代表我当前的子进程


process.on('message', (data) => {
    console.log(data)
})
process.send(msg)
process.on("message",(data)=>{
    const {cmd,url} = data;
    const recLog = log4jInstance()
    switch (cmd) {
        case CMD_CODE.OPEN:
            execFilePromise(url).then((value)=>{
                console.log('子进程发送给父进程')
                process.send(value)
                recLog.send({msg:value})
            }).catch((err)=>{
                process.send(err)
                recLog.send({msg:err,type:'error'})
            })
            break;
        case CMD_CODE.EXIT:
            const { pid } = data;
            console.log('kill',pid)
            kill(pid,(err)=>{
                if(err){
                    process.send({msg:err,code:PROCESS_CODE.ERROR})
                    recLog.send({msg:err,type:'error'})
                }
                else{
                    process.send({msg:"close success",code:PROCESS_CODE.EXIT})
                    recLog.send({msg:`kill ${pid} success`})
                }
            })
            break
        default:
            console.log('default');
            break;
    }
})


log4j日志管理



log4j配置文件


log4js.configure({
  appenders: { cheese: { type: "file", filename: "cheese.log" } },
  categories: { default: { appenders: ["cheese"], level: "error" } },
});


log4j的配置主要分成两个部分,分别是appenders和categories


appenders


appenders配置的是每一种的输出方式,你可以在appenders配置多种输出方式


out: {
    type: 'stdout',
},
dataFile: {
    type: 'dateFile',
    numBackups:7,//7天之前的文件即删除
    filename: '../logs/out.log',
    keepFileExt: true,
    maxLogSize: 20480000,
    backups: 4,
},


比如我这里配置了两种方式分别采用了stdout控制台输出和dataFile文件输出


categories


categories表示你选择你在appenders里面配置的那种输出方式输出


categories: {
    default:{
        appenders:['out','dataFile'],
        level:'debug', // 输出等级,只输出比这个等级高的
    },
},


这里我采用我在appenders里面配置的两种输出方式,即会输出到控制台和out.log这个文件中


实例化logger


// 当我在getLogger()里面不传任何参数的时候,默认对应categories里面default配置
// default配置再会根据我里面的appenders数组,去找appender中out和dataFile的配置
let logger = log4js.getLogger();


我们还可以实例出不同的logger,分别输出不同类型的文件


categories: {
    default: {
      appenders: ['consoleOut', 'default'],
      level: 'all',
    },
    error: {
      appenders: ['consoleOut', 'error'],
      level: 'warn',
    },
  },
 // 普通级别的logger,输出到控制台和日期分类的文件
const defaultLogger = log4js.getLogger('default')
 // 错误信息的logger,输出到控制台和error.log中
const errorLogger = log4js.getLogger('error')


Bug记录



父子进程同时写log4j日志时失败


当我用父子进程进行通信的时候,我需要在两个进程中都要记录一些log4j的日志信息。但是发现只有子进程的日志信息书写进了文件里面,而父进程的日志信息没有记录到。后来发现原来是不能两个进程不能同时写文件,所以我们只好单独开一个进程,把所有的信息交给这个进程来处理。


消息群发???


我们开不同的客户端,理论上应该是只能接收到自己的消息。但是在编码的过程中却发现能获得所有的消息。我们创建了一个父进程,父进程对应多个子进程,但是客户端获得了所有子进程的消息。子进程创建的实例太多,连自己都不清楚谁发送了消息。

改进:将子进程的实例和websocket连接成功后的ws实例储存起来,并生成uuid给定一个唯一的值,这样在收发消息的时候,根据uuid发送给指定的子进程,这样在创建子进程的过程当中,就不会失去对子进程的控制。


相关实践学习
通过日志服务实现云资源OSS的安全审计
本实验介绍如何通过日志服务实现云资源OSS的安全审计。
目录
相关文章
|
安全 Windows
windows11 永久关闭windows defender的方法
windows11 永久关闭windows defender的方法
2084 2
|
计算机视觉 C++ Python
Python+Yolov8手势特征识别检测
这篇博客针对&lt;Python+Yolov8手势特征识别检测&gt;编写代码,代码整洁,规则,易读。 学习与应用推荐首选。
758 0
Python+Yolov8手势特征识别检测
|
Linux 应用服务中间件 nginx
centos 80端口被占用排查+解决
centos 80端口被占用排查+解决
1130 0
|
机器学习/深度学习 算法 自动驾驶
|
缓存 Linux 开发工具
CentOS 7- 配置阿里镜像源
阿里镜像官方地址http://mirrors.aliyun.com/ 1、点击官方提供的相应系统的帮助 :2、查看不同版本的系统操作: 下载源1、安装wget yum install -y wget2、下载CentOS 7的repo文件wget -O /etc/yum.
253238 0
|
Java Linux Shell
java使用JSch连接服务器实现命令交互
这里通过jsch远程连接linux服务器,并在控制台实现命令的交互。
769 0
java使用JSch连接服务器实现命令交互
|
7月前
|
移动开发 安全 虚拟化
VMware ESXi 6.7 U3u (ESXi670-202403001), ESXi 6.5 U3v (ESXi650-202403001) 下载
VMware ESXi 6.7 U3u (ESXi670-202403001), ESXi 6.5 U3v (ESXi650-202403001) 下载
330 1
VMware ESXi 6.7 U3u (ESXi670-202403001), ESXi 6.5 U3v (ESXi650-202403001) 下载
|
11月前
Vue3 中使用 Event Bus
【10月更文挑战第16天】Event Bus 是 Vue3 中一种简单而实用的通信方式,在一些简单的场景中可以发挥重要作用。但在实际应用中,要根据项目的具体需求和复杂度,选择合适的通信方式,以实现最佳的性能和可维护性。同时,要遵循最佳实践,合理使用 Event Bus,避免出现问题。
1167 60
|
11月前
|
机器学习/深度学习 数据采集 算法
如何在一夜之间成为模型微调大师?——从零开始的深度学习修炼之旅,让你的算法功力飙升!
【10月更文挑战第5天】在机器学习领域,预训练模型具有强大的泛化能力,但直接使用可能效果不佳,尤其在特定任务上。此时,模型微调显得尤为重要。本文通过图像分类任务,详细介绍如何利用PyTorch对ResNet-50模型进行微调,包括环境搭建、数据预处理、模型加载与训练等步骤,并提供完整Python代码。通过调整超参数和采用早停策略等技巧,可进一步优化模型性能。适合初学者快速上手模型微调。
602 8
|
11月前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
646 1