溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

發(fā)布時(shí)間:2021-04-19 13:55:55 來(lái)源:億速云 閱讀:574 作者:小新 欄目:web開(kāi)發(fā)

小編給大家分享一下no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

引言

項(xiàng)目需求,要求在瀏覽器端進(jìn)行遠(yuǎn)程桌面的訪(fǎng)問(wèn),如圖所示:

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

實(shí)現(xiàn)遠(yuǎn)程桌面,需要依賴(lài)VNC協(xié)議:

VNC(Virtual Network Computing),為一種使用RFB協(xié)議的屏幕畫(huà)面分享及遠(yuǎn)程操作軟件。此軟件借由網(wǎng)絡(luò),可發(fā)送鍵盤(pán)與鼠標(biāo)的動(dòng)作及即時(shí)的屏幕畫(huà)面。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

相關(guān)的參考比較少,去谷歌搜索出來(lái)的文章大多都是如何使用客戶(hù)端進(jìn)行VNC的搭建與訪(fǎng)問(wèn),很少有將其內(nèi)嵌到web里的,騰訊云有相關(guān)的功能,但因?yàn)闃I(yè)務(wù)安全性,咱也看不著人家咋實(shí)現(xiàn)的。

再見(jiàn),百度。用百度查了一次之后,我才知道原來(lái)VNC是口紅。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

所以VNC實(shí)踐之路就是如下流程:

  1. 根據(jù)自己已有的知識(shí)與技能,設(shè)計(jì)一個(gè)VNC方案。

  2. 嘗試,分析可行性。

  3. 根據(jù)可行性修改方案細(xì)節(jié),或推翻方案重新設(shè)計(jì)。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

從整體的最開(kāi)始設(shè)計(jì),到最終落地方案,大約經(jīng)歷了以下七個(gè)方案的迭代:

  1. SpringBoot調(diào)用REALVNC的C++類(lèi)庫(kù),前后臺(tái)進(jìn)行數(shù)據(jù)交互。失敗,因?yàn)镽EALVNC太貴了,客戶(hù)承受不起。

  2. SpringBoot中模仿TightVNC實(shí)現(xiàn)JavaViewer獲取數(shù)據(jù),前后臺(tái)進(jìn)行數(shù)據(jù)交互。失敗,因?yàn)門(mén)ightVNC JavaViewer的源碼沒(méi)注釋?zhuān)床欢?/p>

  3. SpringBoot中手寫(xiě)VNC客戶(hù)端,前后臺(tái)數(shù)據(jù)交互。失敗,因?yàn)閺?實(shí)現(xiàn)一個(gè)協(xié)議太復(fù)雜了,時(shí)間成本太高。

  4. 瀏覽器端只做VNC鏈接,使用原生客戶(hù)端,直接訪(fǎng)問(wèn)主機(jī)。失敗,需要安裝軟件,且只能訪(fǎng)問(wèn)局域網(wǎng)中的主機(jī)。

  5. 原生客戶(hù)端 + nginx數(shù)據(jù)轉(zhuǎn)發(fā)。失敗,需要安裝軟件,無(wú)法實(shí)現(xiàn)動(dòng)態(tài)轉(zhuǎn)發(fā)(無(wú)法動(dòng)態(tài)變更nginx配置文件)。

  6. no-vnc + nginx數(shù)據(jù)轉(zhuǎn)發(fā)。失敗,無(wú)法實(shí)現(xiàn)動(dòng)態(tài)轉(zhuǎn)發(fā)(無(wú)法動(dòng)態(tài)變更nginx配置文件)。

  7. no-vnc + node.js數(shù)據(jù)轉(zhuǎn)發(fā)。成功,完美實(shí)現(xiàn)。

實(shí)現(xiàn)

思想

整體思想如下圖所示:nginx轉(zhuǎn)發(fā)前臺(tái)的websocket連接,為了實(shí)現(xiàn)外網(wǎng)轉(zhuǎn)發(fā),添加開(kāi)發(fā)的node.js服務(wù)器作為代理,將瀏覽器端no-vnc的websocket數(shù)據(jù)報(bào)在運(yùn)輸層轉(zhuǎn)發(fā)給目標(biāo)主機(jī)。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

why nginx ?

如果思考過(guò)的話(huà),其實(shí)發(fā)現(xiàn)不用nginx也能實(shí)現(xiàn)功能,這里使用nginx主要是減少了前臺(tái)對(duì)后臺(tái)架構(gòu)的耦合。

添加網(wǎng)關(guān)轉(zhuǎn)發(fā)所有請(qǐng)求,對(duì)前臺(tái)只暴露一個(gè)端口,不管后臺(tái)用什么技術(shù),用什么架構(gòu),用什么微服務(wù),在前臺(tái)看來(lái),就好像在訪(fǎng)問(wèn)單體應(yīng)用一樣。

就像目前的華軟項(xiàng)目一樣,后臺(tái)用了spring-boot、.net、node.js,各語(yǔ)言各框架發(fā)揮各自的優(yōu)勢(shì),通過(guò)nginx的轉(zhuǎn)發(fā)將各模塊連接起來(lái),無(wú)論后臺(tái)的架構(gòu)怎么變,對(duì)前臺(tái)毫無(wú)影響,這應(yīng)該是微服務(wù)架構(gòu)的最佳實(shí)踐。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

這是spring官方推薦的微服務(wù)架構(gòu)圖,我們學(xué)習(xí)并實(shí)踐了api網(wǎng)關(guān),spring推薦netflix zuul,我們用的nginx,在請(qǐng)求轉(zhuǎn)發(fā)上,二者性能不相上下。

隨著業(yè)務(wù)需求的增長(zhǎng),我們肯定也會(huì)服務(wù)拆分,服務(wù)注冊(cè),服務(wù)發(fā)現(xiàn),消息隊(duì)列,RPC調(diào)用。然后用上eureka、zookeeper、hystrix、feign等一個(gè)個(gè)優(yōu)秀的開(kāi)源組件,一起探索spring-cloud的最佳實(shí)踐。

websocket

之前一直不了解websocket,就是知道個(gè)名,具體細(xì)節(jié)沒(méi)有學(xué)習(xí)。

http協(xié)議:請(qǐng)求響應(yīng),客戶(hù)端請(qǐng)求,服務(wù)器響應(yīng),一次請(qǐng)求就結(jié)束。服務(wù)端無(wú)法主動(dòng)向客戶(hù)端推送數(shù)據(jù)。

為了解決這個(gè)問(wèn)題,websocket應(yīng)運(yùn)而生。如果所示,不做贅述。

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

no-vnc

官網(wǎng)鏈接:noVNC

no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面

安裝依賴(lài):

npm install @novnc/novnc

前臺(tái)組件

一個(gè)空div,同時(shí)在組件中引用。

<div class="container" #container>
</div>
@ViewChild('container')
private container: ElementRef<HTMLDivElement>;

核心的代碼其實(shí)就這幾行,所有協(xié)議的細(xì)節(jié)都被封裝在no-vnc中的RFB類(lèi)中了。

所有描述以訪(fǎng)問(wèn)192.168.0.104主機(jī)的5900端口為例,websocket地址為:ws://127.0.0.1:8013/vnc/192.168.0.104:5900。

/**
 * VNC連接
 */
private VNCConnect(): void {
  /** 訪(fǎng)問(wèn) /vnc/ websocket */
  const url = `ws://${this.host}/vnc/${this.ip}:${this.port}`;

  /** 新建遠(yuǎn)程控制對(duì)象 */
  this.rfb = new RFB(this.container.nativeElement, url, {
    credentials: {
      password: this.password,
    },
  });

  /** 添加connect事件監(jiān)聽(tīng)器 */
  this.rfb.addEventListener('connect', () => {
    this.rfb.focus();
  });
}

nginx 轉(zhuǎn)發(fā)

nginx監(jiān)聽(tīng)本地的8013端口。

ws://127.0.0.1:8013/vnc/192.168.0.104:5900請(qǐng)求發(fā)給了nginx,根據(jù)前綴匹配,以/vnc/開(kāi)頭的轉(zhuǎn)發(fā)給8112端口。

location /vnc/ {
  proxy_pass http://127.0.0.1:8112/;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection $connection_upgrade;
}

node.js 轉(zhuǎn)發(fā)

node.js監(jiān)聽(tīng)8112端口,處理當(dāng)前的websocket請(qǐng)求。

/** 建立基于 vnc_port 的 websocket 服務(wù)器 */
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);
});

轉(zhuǎn)發(fā)的核心代碼在方法web_socket_handler中,以下是完整代碼:

這里說(shuō)一句,之前寫(xiě)的注釋都不規(guī)范,所有注釋都應(yīng)該是文檔注釋?zhuān)瑔涡凶⑨屖褂?** 內(nèi)容 */的格式。

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

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

/** 引入 websocket 類(lèi) */
const WebSocketServer = require('ws').Server;

/** 本機(jī) ip 地址 */
const localhost = '127.0.0.1';

/** 開(kāi)放的 vnc websocket 轉(zhuǎn)發(fā)端口 */
const vnc_port = '8112';

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

/** 建立基于 vnc_port 的 websocket 服務(wù)器 */
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) {
  /** 獲取請(qǐng)求url */
  const url = req.url;

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

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

  /** 打印日志 */
  console.log(`WebSocket 連接 : 版本 ${client.protocolVersion}, 協(xié)議 ${client.protocol}`);

  /** 連接到 VNC Server */
  const target = net.createConnection(port, host, function () {
    console.log('連接至目標(biāo)主機(jī)');
  });

  /** 數(shù)據(jù)事件 */
  target.on('data', function (data) {
    try {
      client.send(data);
    } catch (error) {
      console.log('客戶(hù)端已關(guān)閉,清理到目標(biāo)主機(jī)的連接');
      target.end();
    }
  });

  /** 結(jié)束事件 */
  target.on('end', function () {
    console.log('目標(biāo)主機(jī)已關(guān)閉');
    client.close();
  });

  /** 錯(cuò)誤事件 */
  target.on('error', function () {
    console.log('目標(biāo)主機(jī)連接錯(cuò)誤');
    target.end();
    client.close();
  });

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

  /** 關(guān)閉事件 */
  client.on('close', function (code, reason) {
    console.log(`WebSocket 客戶(hù)端斷開(kāi)連接:$[code] [${reason}]`);
    target.end();
  });

  /** 錯(cuò)誤事件 */
  client.on('error', function (error) {
    console.log(`WebSocket 客戶(hù)端出錯(cuò):${error}`);
    target.end();
  });
};

以上是“no-vnc和node.js如何實(shí)現(xiàn)web遠(yuǎn)程桌面”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI