美文网首页
vue+noVNC实现VNC客户端

vue+noVNC实现VNC客户端

作者: YiYaYiYaHei | 来源:发表于2021-11-23 15:01 被阅读0次

最近遇到一个需求:虚拟机运行成功后,需要在前端界面上弹出虚拟机的远程桌面(类似VNC客户端),在此做个记录~

1. 实现流程
图1-1 实现思想
连接地址:ws://localhost:8081/vnc/192.168.18.57:5900,使用nginx代理vnc至node服务,在转发至目标主机,也就是连接的虚拟机地址192.168.18.57:5900
2. 前端

安装 npm install @novnc/novnc

<template>
  <div class="full">
    <div id="screen" class="full"></div>
  </div>
</template>

<script>
import RFB from '@novnc/novnc/core/rfb';

export default {
  name: 'Novnc',
  data() {
    return {
      url: '',
      rfb: null
    }
  },
  methods: {
    getUrl(host) {
      let protocol = '';
      if (window.location.protocol === 'https:') {
        protocol = 'wss://';
      } else {
        protocol = 'ws://';
      }
      // 加window.location.host可以走vue.config.js的代理,ws://localhost:8081/vnc/192.168.18.57:5900
      const wsUrl = `${protocol}${window.location.host}/vnc/${host}`;
      console.log(wsUrl);
      return wsUrl;
    },
    // vnc连接断开的回调函数
    disconnectedFromServer(msg) {
      console.log('断开连接', msg);
      // clean是boolean指示终止是否干净。在发生意外终止或错误时 clean将设置为 false。
      if(msg.detail.clean){
        // 根据 断开信息的msg.detail.clean 来判断是否可以重新连接
      } else {
        // 这里做不可重新连接的一些操作
        console.log('连接不可用(可能需要密码)')
      }
      this.rfb = null;
      this.connectVnc();
    },
    // 连接成功的回调函数
    connectedToServer() {
      console.log('连接成功');
    },
    //连接vnc的函数
    connectVnc() {
      const PASSWORD = '';
      let rfb = new RFB(document.getElementById('screen'), this.url, {
        // 向vnc 传递的一些参数,比如说虚拟机的开机密码等
        credentials: {password: PASSWORD}
      });
      rfb.addEventListener('connect', this.connectedToServer);
      rfb.addEventListener('disconnect', this.disconnectedFromServer);
      // scaleViewport指示是否应在本地扩展远程会话以使其适合其容器。禁用时,如果远程会话小于其容器,则它将居中,或者根据clipViewport它是否更大来处理。默认情况下禁用。
      rfb.scaleViewport = true;
      // 是一个boolean指示是否每当容器改变尺寸应被发送到调整远程会话的请求。默认情况下禁用
      rfb.resizeSession = true;
      this.rfb = rfb;
    }
  },
  mounted() {
    this.url = this.getUrl(this.$route.params.host);
    this.connectVnc();
  },
  beforeDestroy() {
    this.rfb && this.rfb.disconnect();
  }
}
</script>
2. node
/** 引入 http 包 */
const http = require('http');

/** 引入 net 包 */
const net = require('net');

/** 引入 websocket 类 */
const WebSocketServer = require('ws').Server;

/** 本机 ip 地址 */
const localhost = '127.0.0.1';

/** 开放的 vnc websocket 转发端口 */
const vnc_port = 8112;

/** 打印提示信息 */
console.log(`成功创建 WebSocket 代理 : ${localhost} : ${vnc_port}`);

/** 建立基于 vnc_port 的 websocket 服务器 */
const vnc_server = http.createServer();
vnc_server.listen(vnc_port, function () {
    const web_socket_server = new WebSocketServer({server: vnc_server});
    web_socket_server.on('connection', web_socket_handler);
});

/** websocket 处理器 */
const web_socket_handler = function (client, req) {
    /** 获取请求url */
    const url = req.url;
    console.log("====", url);

    /** 截取主机地址 */
    const host = url.substring(url.indexOf('/') + 1, url.indexOf(':'));

    /** 截取端口号 */
    const port = Number(url.substring(url.indexOf(':') + 1));

    /** 打印日志 */
    console.log(`WebSocket 连接 : 版本 ${client.protocolVersion}, 协议 ${client.protocol}`);

    /** 连接到 VNC Server */
    const target = net.createConnection(port, host, function () {
        console.log('连接至目标主机');
    });

    /** 数据事件 */
    target.on('data', function (data) {
        try {
            client.send(data);
        } catch (error) {
            console.log('客户端已关闭,清理到目标主机的连接');
            target.end();
        }
    });

    /** 结束事件 */
    target.on('end', function () {
        console.log('目标主机已关闭');
        client.close();
    });

    /** 错误事件 */
    target.on('error', function () {
        console.log('目标主机连接错误');
        target.end();
        client.close();
    });

    /** 消息事件 */
    client.on('message', function (msg) {
        target.write(msg);
    });

    /** 关闭事件 */
    client.on('close', function (code, reason) {
        console.log(`WebSocket 客户端断开连接:${code} [${reason}]`);
        target.end();
    });

    /** 错误事件 */
    client.on('error', function (error) {
        console.log(`WebSocket 客户端出错:${error}`);
        target.end();
    });
};
3. nginx
location /vnc/ {
  # rewrite         ^.+iot/?(.*)$ /$1 break;
  add_header      Access-Control-Allow-Origin *;
  add_header      Access-Control-Allow-Headers "Accept, X-Token, Content-Type";
  add_header      Access-Control-Allow-Methods "GET, POST, DELETE, PATCH, PUT, OPTIONS";
  proxy_pass      http://127.0.0.1:8112/;

  # (以下2句)配置允许创建websocket
  proxy_set_header Upgrade websocket;
  proxy_set_header Connection Upgrade;
}
4. 结果
图4-1 运行结果截图
参考文章

no-vnc和node.js实现web远程桌面的完整步骤

相关文章

网友评论

      本文标题:vue+noVNC实现VNC客户端

      本文链接:https://www.haomeiwen.com/subject/ysubtrtx.html