webssh-xterm.js的簡(jiǎn)單使用

xterm是一個(gè)使用TypeScript編寫(xiě)的前端終端組件。

1、demo主要介紹

本文demo主要實(shí)現(xiàn)一個(gè)頁(yè)面一個(gè)webssh窗口,前端部分主要利用xterm通過(guò)原生ws和后端通信,后端部分使用nodejs+utf8+ws+ssh2。

頁(yè)面效果

簡(jiǎn)要流程

2、前端實(shí)現(xiàn)

基于vue項(xiàng)目,前端主要依賴包:xterm xterm-addon-fit,使用前請(qǐng)install。

<template>
  <div id="xterm"/>
</template>

<script>
import 'xterm/css/xterm.css'
import { Terminal } from 'xterm';
import { FitAddon } from "xterm-addon-fit";

export default {
  name: 'WebShell',
  data() {
    return {
      socketURI: 'ws://localhost:2000/'
    }
  },
  mounted() {
    this.initSocket()
  },
  beforeDestroy() {
    this.socket.close()
    this.term && this.term.dispose()
  },
  methods: {
    initTerm() {
      let element = document.getElementById('xterm');
      // 設(shè)置了cols或者fitAddon.fit(); 當(dāng)一行字符超過(guò)80個(gè)過(guò)會(huì)替換現(xiàn)在的內(nèi)容,否則換行
      const term = new Terminal({
        cursorBlink: true, // 關(guān)標(biāo)閃爍
        cursorStyle: "underline", // 光標(biāo)樣式 'block' | 'underline' | 'bar'
        scrollback: 100, // 當(dāng)行的滾動(dòng)超過(guò)初始值時(shí)保留的行視窗,越大回滾能看的內(nèi)容越多,
      });
      this.term = term;
      const fitAddon = new FitAddon();
      this.term.loadAddon(fitAddon);
      this.fitAddon = fitAddon;
      term.open(element);
      // 自適應(yīng)大小(使終端的尺寸和幾何尺寸適合于終端容器的尺寸),初始化的時(shí)候?qū)捀叨际菍?duì)的
      fitAddon.fit();
      term.focus();

      this.term.onData(data =>  {
        this.socket.send(data);
      });

      window.addEventListener('resize', this.resizeTerm);
    },
    getColsAndRows(element) {
      // 暫時(shí)不用
      element = element || document.getElementById('xterm');
      return {
        rows: parseInt((element.clientHeight - 0) / 18),
        cols: 10 // parseInt(element.clientWidth / 8)
      };
    },
    resizeTerm() {
      this.fitAddon.fit();
      this.term.scrollToBottom();
    },
    initSocket() {
      this.socket = new WebSocket(this.socketURI);
      this.socketOnClose();
      this.socketOnOpen();
      this.socketOnError();
      this.socketOnMessage();
    },
    socketOnOpen() {
      this.socket.onopen = () => {
        // 連接成功后
        this.initTerm()
      }
    },
    socketOnMessage() {
      this.socket.onmessage = (event) => {
        // 接收推送的消息
        this.term.write(event.data.toString());
      }
    },
    socketOnClose() {
      this.socket.onclose = () => {
        console.log('close socket')
      }
    },
    socketOnError() {
      this.socket.onerror = () => {
        console.log('socket error')
      }
    }
  }
}
</script>

<style scoped>
#xterm {
  padding: 15px 0;
}
</style>

3、后端實(shí)現(xiàn)

前端主要依賴包:utf8 ssh2 ws,使用前請(qǐng)install。
ssh2用來(lái)實(shí)現(xiàn)nodejs和服務(wù)器進(jìn)行連接和通信。
utf8用來(lái)實(shí)現(xiàn)服務(wù)器返回的命令執(zhí)行結(jié)果解碼。
ws用來(lái)實(shí)現(xiàn)后端和前端ws全雙工通信。

/**用來(lái)實(shí)現(xiàn)單個(gè)webssh功能**/
const utf8 = require("utf8");
const SSHClient = require("ssh2").Client;
const Server = require("ws").Server;
const wss = new Server({
  port: 2000
});

const serverInfo = {
  host: '192.168.18.141',
  port: 22,
  username: 'root',
  password: '密碼不告訴你.'
};

function createSocket(ws) {
  const ssh = new SSHClient();
  ssh
    .on("ready", function () {
      ws.send("\r\n*** SSH CONNECTION ESTABLISHED ***\r\n");
      ssh.shell(function (err, stream) {
        if (err) {
          return ws.emit("\r\n*** SSH SHELL ERROR: " + err.message + " ***\r\n");
        }
        ws.on("message", function (data) {
          stream.write(data);
        });
        ws.on("close", function () {
          console.log("close websocket。");
          ssh.end();
        });
        stream
          .on("data", function (d) {
            let data = utf8.decode(d.toString("binary"));
            ws.send(data);
          })
          .on("close", function () {
            ssh.end();
          });
      });
    })
    .on("close", function () {
      ws.close();
    })
    .on("error", function (err) {
      console.log("\r\n*** SSH CONNECTION ERROR: " + err.message + " ***\r\n");
      ws.close();
    })
    .connect(serverInfo);
}

wss.on("connection", function (ws) {
  createSocket(ws);
});

4、遺留問(wèn)題

1、瀏覽器resize后,webshell窗口寬高自適應(yīng)、命令顯示的問(wèn)題;
2、設(shè)置了cols或者fitAddon.fit(); 當(dāng)一行字符超過(guò)80個(gè)后,會(huì)替換現(xiàn)在的內(nèi)容問(wèn)題。

5、友情鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 最近由于項(xiàng)目需求,項(xiàng)目中需要實(shí)現(xiàn)一個(gè)WebSSH連接終端的功能,由于自己第一次做這類型功能,所以首先上了Gi...
    ObjectSpace閱讀 1,104評(píng)論 0 3
  • 為什么學(xué)習(xí)Python? 通過(guò)什么途徑學(xué)習(xí)的Python? 上網(wǎng)收集視頻,資料 關(guān)注公證號(hào) 買教程,書(shū)籍 Pyth...
    130920閱讀 1,414評(píng)論 0 0
  • 1、談?wù)剬?duì)http協(xié)議的認(rèn)識(shí)流程:1.域名解析域名解析檢查順序?yàn)椋簽g覽器自身DNS緩存---》OS自身的DNS緩存...
    Zzmi閱讀 869評(píng)論 0 0
  • 今天感恩節(jié)哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開(kāi)了第一次的黨會(huì),身份的轉(zhuǎn)變要...
    余生動(dòng)聽(tīng)閱讀 10,798評(píng)論 0 11
  • 彩排完,天已黑
    劉凱書(shū)法閱讀 4,452評(píng)論 1 3

友情鏈接更多精彩內(nèi)容