前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何实现在线web terminal

如何实现在线web terminal

作者头像
前端知知
发布2022-09-29 19:22:20
1.6K0
发布2022-09-29 19:22:20
举报
文章被收录于专栏:前端知知前端知知

1.前言

随着云服务、DevOps等兴起,我们经常在网站中看到远程登录功能,点击之后会出现一个类似Shell的终端,可以查看相关服务状态,编写代码等功能。web termial让我们和实际的机器通过网络联通了。

在生产环境中进行登录和调试都非常方便,那么这是如何实现的呢,要解决两个问题:实现 Shell 界面以及浏览器与服务器进行通信。

2.如何实现Shell 界面

在react 中有很多模拟 Terminal 组件库,比如 [react-terminal]1

[terminal-in-react] 2等,笔者推荐的是使用xterm 3, 其可以满足以下需求:

  • 丰富的指令
  • 支持多种编码
  • 支持大多数终端程序如:vim,bash等

日常开始使用的IDE VS Code也是用xterm,可见应用的广泛性。使用如下。

2.1 Terminal 构造函数

Terminal 对象作为一个构造函数,用于新建 Terminal 实例。

代码语言:javascript
复制
import { Terminal } from "xterm";
let terminal = new Terminal({ cursorBlink: true });
2.2 Terminal.open

将terminal实例附加到指定的dom 元素上。

代码语言:javascript
复制
terminal.open(document.getElementById("terminal"));
2.3 Terminal.loadAddon

使用terminal 提供的插件扩展功能

代码语言:javascript
复制
terminal.loadAddon(fitAddon);

3.如何进行通信

日常开发中大多数使用的是http协议,但是有个缺点是只能客户端向服务端发送请求,服务器返回结果;无法web terminal 也会出现服务端推送消息的情况。

WebSocket4 协议在2008年诞生,现在浏览器都已经支持。其特点是服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息。

3.1 与服务器建立连接

客户端提供 WebSoket API,通创建一个实例就可以将客户端与服务器端连接。

代码语言:javascript
复制
ws = new WebSocket("wss:xxx");
3.2 向服务端发送消息

websocket.send 方法可以发送消息给服务器

代码语言:javascript
复制
ws.send("Hello WebSockets!");
3.3 接收服务端消息

websocket.onmessage 可接收服务端消息

代码语言:javascript
复制
ws.onmessage = function(event) {
  var data = event.data;
  // 处理数据
};

4. 具体实现

在解决展示和通信问题之后,将两者结合起来,就可实现一个在线web terminal 组件了,大体代码如下。

4.1 创建通信类
代码语言:javascript
复制
//TerminalConnector.js
export class TerminalConnector {
 private ws;
  constructor({
    command,
    handleOpen,
    handleMessage,
    handleClose,
    handleError,
  }) {
      this.handleOpen = handleOpen;
      this.handleMessage = handleMessage;
      this.handleClose = handleClose;
      this.handleError = handleError;
      this.ws = new WebSocket("wss:xxx");
      this.ws.onopen = this.onConnOpen;
      this.ws.onmessage = this.onMessage;
      this.ws.onclose = this.onConnClose;
      this.ws.onerror = this.onConnError;
    }
    handleSend(data) {
     this.ws.send(data);
   }
    detachTerminal() {
      try {
        this.ws.close();
        this.ws = null;
      } catch (e) {
        console.warn(e);
      }
    }
    private onConnOpen = () => {
     //建立连接后身份验证
     this.ws.send('authToken相关数据')
    }
    private onMessage = (event) => {
     const dat = event.data || '';
     this.handleMessage(data);
    }
    private onConnClose = (e) => {
       console.log("conn close", e);
       this.handleClose();
    }
    private onConnError = (e) => {
      console.error("conn error", e);
      this.handleError(e);
    }

}
4.2 实现 Teraminal组件
代码语言:javascript
复制
import React from "react";
import { Terminal } from "xterm";
import { FitAddon } from "xterm-addon-fit";
import {
  TerminalConnector
} from "./TerminalConnector";

export function Xterm(props) {
  let { command } = props;

  const [curTerminal, setTerminal] = React.useState(null);
  const [curConnector, setConnector] = React.useState(null);

  React.useEffect(() => {
    if (curTerminal) {
      curTerminal.dispose();
    }
    if (curConnector) {
      curConnector.detachTerminal();
    }
    let terminal = new Terminal({ cursorBlink: true });
    let fitAddon = new FitAddon();
    let connector = new TerminalConnector({
      command,
      handleOpen,
      handleMessage,
      handleClose,
      handleError,
    });
    setTerminal(terminal);
    setConnector(connector);
    function handleOpen() {
      fitAddon.fit();
      terminal.focus();

      window.onresize = function () {
        fitAddon.fit();
      };
    }

    function handleMessage(data) {
     // 接收服务器数据,将数据写入shell
       terminal.write(data);
       
    }

    function handleClose() {
      terminal.writeln("Exit");
    }

    function handleError(e) {
      terminal.writeln("");
      terminal.writeln("Sorry, connection has some error.");
    }

    terminal.loadAddon(fitAddon);
    terminal.open(document.getElementById("terminal"));
    terminal.onResize(({ cols, rows }) => {
      connector.handleSend(
        JSON.stringify({ columns: cols, rows })
      );
    });
    terminal.onData((data) => {
      connector.handleSend(data);
    });
  }, [command]);

  return <div id="terminal"></div>;
}

[1]react-terminal: https://www.npmjs.com/package/react-terminal

[2]terminal-in-react: https://github.com/nitin42/terminal-in-react

[3]xterm:https://xtermjs.org/

[4]WebSoket: https://www.ruanyifeng.com/blog/2017/05/websocket.html

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-07-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端知知 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2.如何实现Shell 界面
    • 2.1 Terminal 构造函数
      • 2.2 Terminal.open
        • 2.3 Terminal.loadAddon
        • 3.如何进行通信
          • 3.1 与服务器建立连接
            • 3.2 向服务端发送消息
              • 3.3 接收服务端消息
              • 4. 具体实现
                • 4.1 创建通信类
                  • 4.2 实现 Teraminal组件
                  相关产品与服务
                  多因子身份认证
                  多因子身份认证(Multi-factor Authentication Service,MFAS)的目的是建立一个多层次的防御体系,通过结合两种或三种认证因子(基于记忆的/基于持有物的/基于生物特征的认证因子)验证访问者的身份,使系统或资源更加安全。攻击者即使破解单一因子(如口令、人脸),应用的安全依然可以得到保障。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档