Flask 运用Xterm实现交互终端

news2024/9/30 17:29:04

Xterm是一个基于X Window System的终端仿真器(Terminal Emulator)。Xterm最初由MIT开发,它允许用户在X Window环境下运行文本终端程序。Xterm提供了一个图形界面终端,使用户能够在图形桌面环境中运行命令行程序。而xterm.js是一个用于在浏览器中实现终端仿真的JavaScript库。它允许在Web页面中创建交互式的终端界面,用户可以在浏览器中运行命令行程序,执行命令,并与终端进行交互。

主要特点和功能包括:

  1. 终端仿真: xterm.js通过JavaScript模拟了一个终端环境,支持常见的终端功能,包括光标移动、颜色控制、滚动等。
  2. 多平台支持: 由于是基于JavaScript实现,xterm.js可以在各种现代浏览器上运行,无论是在桌面还是移动设备上。
  3. 自定义外观: xterm.js提供了丰富的配置选项,用户可以定制终端的外观和行为,包括颜色、字体、光标样式等。
  4. 剪贴板支持: 支持从终端复制文本到剪贴板,并从剪贴板粘贴文本到终端。
  5. WebSockets和其他集成: 可以与WebSockets等通信协议集成,以便在浏览器中实现实时的终端交互。
  6. 支持Unicode和UTF-8: 能够正确显示和处理Unicode字符,支持UTF-8编码。

xterm.js通常被用于Web应用程序中,尤其是在需要提供命令行界面的场景下,如在线终端、远程服务器管理等。这使得开发者能够在浏览器中实现类似于本地终端的交互体验,而无需使用本地终端模拟器。

AJAX 实现Web交互

AJAX(Asynchronous JavaScript and XML)是一种用于在Web应用程序中实现异步数据交换的技术。它允许在不重新加载整个页面的情况下,通过在后台与服务器进行小规模的数据交换,实现动态更新网页内容的目的。AJAX广泛用于创建交互性强、用户体验良好的Web应用程序,例如在加载新数据、进行表单验证、实现自动完成搜索等方面。

如下前端部分,通过使用ajax向后端提交数据,当success:function接收到数据后直接将数据动态回写到Xterm终端上,代码如下所示;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://www.lyshark.com/javascript/xterm/xterm.css" />
    <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script>
    <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
    <script type="text/javascript">
      var window_width = $(window).width()-200;
      var window_height = $(window).height()-300;
      var term = new Terminal(
            {
                cols: Math.floor(window_width/9),
                rows: Math.floor(window_height/20),
                useStyle:false,
                convertEol: true,
                cursorBlink:false,
                cursorStyle:null,
                rendererType: "canvas",
            }
    );
    term.open(document.getElementById('terminal'));
      function show(){
          var address = $("#address").val();
          var command = $("#command").val();
          console.log(command);
          $.ajax({
              url:"/",
              type:"POST",
              contentType:"application/json;",
              data: JSON.stringify({"address":address,"command":command}),
              success:function (res)
              {
                  // term.clear();
                  term.writeln( "\x1B[1;3;33m IP地址: \x1B[0m" + res.address );
                  term.writeln( "\x1B[1;3;34m 命令: \x1B[0m" + res.command );
              }
          });
      }
    </script>

    <!--提交数据-->
    <div id="terminal"></div>
        <input type="text" id="address" placeholder="主机地址"/>
        <input type="text" id="command" placeholder="执行命令"/>
        <input type="button" value="执行命令" onclick="show()">
    </div>
</body>
</html>

后端部分的实现很简单,首先封装一个ssh_shell用于执行命令,用户传入数据后,直接执行并将返回结果放入到ref内即可。

from flask import Flask,render_template,request
from flask import jsonify
import paramiko

app = Flask(__name__)

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

def ssh_shell(address,username,password,port,command):
    ssh.connect(address,port=port,username=username,password=password)
    stdin, stdout, stderr = ssh.exec_command(command)
    result = stdout.read()
    if not result:
        result=stderr.read()
    ssh.close()
    return result.decode()

@app.route('/', methods=[ 'GET', 'POST'])
def index():
    if request.method == "POST":
        # 接收数据
        json_value = request.get_json()
        ref = ssh_shell("192.168.150.128","root","123123","22",json_value["command"])

        # 发送数据
        info = dict()
        info["address"] = json_value["address"]
        info["command"] = ref
        return jsonify(info)
    else:
        return render_template("index.html")

if __name__ == '__main__':
    app.run()

AJAX实现Web终端

继续扩展将编辑框去掉,用户输入数据后直接传入到Xterm内,Xterm里卖弄判断如果出现了回车,则像后端发送ajax数据,否则继续侦听并记下输入数据。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://www.lyshark.com/javascript/xterm/xterm.css" />
    <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script>
    <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script>
</head>
<body>
    <div id="terminal"></div>

    <script type="text/javascript">
        var window_width = $(window).width()-500;
        var window_height = $(window).height()-300;
        var term = new Terminal
        (
            {
                cols: Math.floor(window_width/9),
                rows: Math.floor(window_height/20),
                useStyle:false,
                convertEol: true,
                cursorBlink: true,        //光标闪烁
                cursorStyle: "underline", //光标样式
                rendererType: "canvas",
            }
        );
        term.open(document.getElementById('terminal'));
        term.writeln("welcome to lyshark web terminal!");
        term.write("[shell] # ");

        let input = '';
        term.on('key', (key, ev) => {
          let code = key.charCodeAt(0);
          console.log(code);

          // 如果按下回车,则发送命令,并等待输出结果
          if(code == 13)
          {
              term.write("\r\n");
              $.ajax({
                  url:"/",
                  type:"POST",
                  contentType:"application/json;",
                  data: JSON.stringify({"command": input}),
                  success:function (res)
                  {
                      term.write(res.value);
                  }
              });
              input ='';
          }
          // 如果是退格,则清除
          else if(code == 127)
          {
              term.write("\b");
          }
          else
          {
              input += key
              term.write(key);
          }
        });
    </script>
</body>
</html>

后端收到数据后解析命令,比对命令是否存在,根据不同的命令执行不同的分支。

from flask import Flask,render_template,request
from flask import jsonify

app = Flask(__name__)

@app.route('/', methods=[ 'GET', 'POST'])
def index():
    if request.method == "POST":
        # 接收数据
        json_value = request.get_json()["command"]
        if len(json_value) != 0:

            # 判断使用哪一个分支
            splite_value = json_value.split(" ")
            info = dict()

            if splite_value[0] == "help":
                info["value"] = "version 1.0"
                info["value"] = info["value"] + "\n[shell] # "
                return jsonify(info)

            elif splite_value[0] == "GetCPU":
                address = splite_value[1]
                info["value"] = "192.168.1 CPU 10%"
                info["value"] = info["value"] + "\n[shell] # "
                return jsonify(info)
            else:
                info["value"] = "命令不存在"
                info["value"] = info["value"] + "\n[shell] # "
                return jsonify(info)
        else:
            info = dict()
            info["value"] = "[shell] # "
            return jsonify(info)
    else:
        return render_template("index.html")

if __name__ == '__main__':
    app.run()

运行后可输出一个交互式WebShell环境,如下图所示;

WebSocket 实现终端

虽然WebSSH可以方便管理主机,但如果需要批量运维则需要开发一个可以多条消息共同推送的命令行。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://www.lyshark.com/javascript/xterm/xterm.css" />
    <script type="text/javascript" src="https://www.lyshark.com/javascript/xterm/xterm.js"></script>
    <script type="text/javascript" src="https://www.lyshark.com/javascript/jquery/3.5.1/jquery.min.js"></script>
    <script type="text/javascript" src="https://www.lyshark.com/javascript/socket.io/socket.io.min.js"></script>
</head>

<body>
    <div id="terminal"></div>

    <script type="text/javascript" charset="UTF-8">
        $(document).ready(function()
        {
            namespace = '/Socket';
            var socket = io.connect("http://" + document.domain + ":" + location.port + namespace);

            var window_width = $(window).width()-500;
            var window_height = $(window).height()-300;
            var term = new Terminal
            (
                {
                    cols: Math.floor(window_width/9),
                    rows: Math.floor(window_height/20),
                    useStyle:false,
                    convertEol: true,
                    cursorBlink: true,
                    rendererType: "canvas",
                }
            );

            // 打开Web终端
            term.open(document.getElementById('terminal'));
            term.write("[shell] # ");

            let input_command = '';

            term.on('key', (key, ev) => {
              let code = key.charCodeAt(0);
              console.log(code);

              // 如果按下回车,则发送命令,并等待输出结果
              if(code == 13)
              {
                  // 发送数据到后端
                  term.write("\r\n");
                  socket.emit("message",{"command": input_command});
                  input_command ='';
              }
              // 如果是退格,则清除
              else if(code == 127)
              {
                  term.write("\b");
              }
              else
              {
                  input_command += key
                  term.write(key);
              }
            });

            // 接受后台返回并输出
            socket.on('response', function(recv)
            {
                console.log(recv.value);
                term.write(recv.value);
            });
        });
    </script>
</body>
</html>

后台接收参数,并更具不同的参数执行不同的运维函数,此处只做演示,具体功能需要自行编写。

from flask import Flask,render_template,request
from flask_socketio import SocketIO

async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = "lyshark"
socketio = SocketIO(app)

@app.route("/")
def index():
    return render_template("index.html")

# 出现消息后,率先执行此处
@socketio.on("message",namespace="/Socket")
def socket(message):
    print("接收到消息:",message['command'])

    command = message['command']

    if len(command) != 0:
        splite_command = command.split(" ")

        if splite_command[0] == "help":
            socketio.emit("response", {"value": "version 1.0 \n"}, namespace="/Socket")

        elif splite_command[0] == "Ping":
            if len(splite_command) == 2:
                index = splite_command[1]
                for each in range(int(index)):
                    socketio.sleep(0.1)
                    socketio.emit("response",{"value": str(each) + "\n"}, namespace="/Socket")
                socketio.emit("response", {"value": "\n[shell] # "}, namespace="/Socket")
        else:
            socketio.emit("response", {"value": "lyShell: command not found \n"}, namespace="/Socket")
    else:
        socketio.emit("response", {"value": "[shell] # "}, namespace="/Socket")

# 当websocket连接成功时,自动触发connect默认方法
@socketio.on("connect",namespace="/Socket")
def connect():
    print("链接建立成功..")

# 当websocket连接失败时,自动触发disconnect默认方法
@socketio.on("disconnect",namespace="/Socket")
def disconnect():
    print("链接建立失败..")

if __name__ == '__main__':
    socketio.run(app,debug=True)

Socket版本的将会更流畅,如下图所示;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1261585.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物理世界中的等距3D对抗样本

论文题目&#xff1a;Isometric 3D Adversarial Examples in the Physical World 会议&#xff1a;NIPS 2022 点云&#xff1a; 点云——表达目标空间分布和目标表面特性的海量点集合&#xff0c;点包含xyz坐标信息 能够包含颜色等其他信息 使用顶点、边和面的数据表征的三维…

【操作宝典】SQL巨擘:掌握SQL Server Management的终极秘籍!

目录 ⛳️【SQL Server Management】 ⛳️1. 启动登录 ⛳️2. 忘记密码 ⛳️3. 操作数据库和表 3.1 新建数据库text 3.2 新建表 3.3 编辑表 3.4 编写脚本 ⛳️【SQL Server Management】 ⛳️1. 启动登录 需要开启服务 ⛳️2. 忘记密码 登录windows--> 安全性 -->…

基于Java SSM框架+Vue留学生交流互动论坛网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现学生交流互动论坛网站演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所…

四川开启智能巡河形式,无人机水利行业应用再创新

在四川省某区域&#xff0c;复亚智能无人机系统以其独特的机场网格化部署得到成功应用&#xff0c;覆盖了该区域内多条市级、省级河流&#xff0c;成为水利行业的新亮点。这一先进系统以无人机水利行业应用为核心&#xff0c;通过网格化和信息化手段&#xff0c;实现了对水域环…

Java核心知识点整理大全19-笔记

目录 14.1.5.2. MemStore 刷盘 全局内存控制 MemStore 达到上限 RegionServer 的 Hlog 数量达到上限 手工触发 关闭 RegionServer 触发 Region 使用 HLOG 恢复完数据后触发 14.1.6.HBase vs Cassandra 15. MongoDB 15.1.1. 概念 15.1.2. 特点 16. Cassandra 16.1.1…

activate jrebel JRebel激活过程

1.下载反向代理 地址&#xff1a;https://pan.baidu.com/s/1wklvDtyrSBXE4I6lKCxXBg?pwdidos 下载完后双击运行 2.在idea中如下步骤点击&#xff1a;File ——> Setting... ——> JRebel ——> Activate now 填入Team URL 1.http://127.0.0.1:8888/uuid //这个…

用于图像分类任务的经典神经网络综述

&#x1f380;个人主页&#xff1a; https://zhangxiaoshu.blog.csdn.net &#x1f4e2;欢迎大家&#xff1a;关注&#x1f50d;点赞&#x1f44d;评论&#x1f4dd;收藏⭐️&#xff0c;如有错误敬请指正! &#x1f495;未来很长&#xff0c;值得我们全力奔赴更美好的生活&…

电脑如何录音?适合初学者的详细教程

“电脑怎么录音呀&#xff1f;参加了一个学校举办的短视频大赛&#xff0c;视频拍摄都很顺利&#xff0c;音乐却出了问题&#xff0c;朋友说可以用电脑录制一段音乐应付一下&#xff0c;可是我不会操作&#xff0c;有哪位大佬教教我&#xff01;” 声音是一种强大的媒介&#…

回归预测 | MATLAB实现SMA+WOA+BOA-LSSVM基于黏菌算法+鲸鱼算法+蝴蝶算法优化LSSVM回归预测

回归预测 | MATLAB实现SMAWOABOA-LSSVM基于黏菌算法鲸鱼算法蝴蝶算法优化LSSVM回归预测 目录 回归预测 | MATLAB实现SMAWOABOA-LSSVM基于黏菌算法鲸鱼算法蝴蝶算法优化LSSVM回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 MATLAB实现SMAWOABOA-LSSVM基于黏菌算法…

Ubuntu18.04磁盘取证-中难度篇

涉及的镜像文件&#xff1a; sdb.vhd uac.tar ubuntu.20211208.mem 需要利用的工具&#xff1a; volatility3 volatility2.6.1 FTK/Autopsy Strings 题干 容器是一个Ubuntu Linux 蜜罐&#xff0c;用来观察利用 CVE-2021-41773 的漏洞攻击者想要做什么。 您将看到一个 cr…

高仿IT之家微信小程序(附精选源码32套,涵盖商城团购等)

项目预览 主要包含主页资讯&#xff0c;圈子俩大模块 主页 资讯详情 圈子 相关代码 网络请求 import wx from wx import Fly from flyioconst request new Fly()request.interceptors.request.use((request) > {wx.showNavigationBarLoading()return request })requ…

Linux - 动静态库(下篇)

前言 在上篇博客当中&#xff0c;对静态库是什么&#xff0c;怎么使用&#xff0c;简单实现自己的静态库&#xff0c;这些做了描述&#xff0c;具体请看上篇博客&#xff1a; 本篇博客将会对 动态库是什么&#xff0c;怎么使用&#xff0c;简单实现自己的动态库&#xff0c…

什么是yum?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

浅谈安科瑞多功能仪表和网关在中国香港某项目的应用

摘要&#xff1a;本文介绍了安科瑞多功能电能表在中国香港某项目的应用。APM系列交流多功能仪表是一款专门为电力系统、工矿企业、公用事业和智能建筑用于电力监控而设计的智能电表。 Abstract&#xff1a;This article introduces the application of the IoT power meter in…

AI超级个体:ChatGPT与AIGC实战指南

目录 前言 一、ChatGPT在日常工作中的应用场景 1. 客户服务与支持 2. 内部沟通与协作 3. 创新与问题解决 二、巧用ChatGPT提升工作效率 1. 自动化工作流程 2. 信息整合与共享 3. 提高决策效率 三、巧用ChatGPT创造价值 1. 优化产品和服务 2. 提高员工满意度和留任率…

vue3中的Fragment、Teleport、Suspense新组件

Fragment组件 在Vue2中: 组件必须有一个根标签 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 好处: 减少标签层级, 减小内存占用 <template><div style"font-size: 14px;"><p> 组件可以没有根标签</p&g…

EtherCAT从站XML文件组成元素详解(2):状态机

0 工具准备 1.EtherCAT从站XML文件(本文使用DM3E-556) 2.ETG.2000 S (R) V1.0.71 前言 EtherCAT从站的设备描述文件ESI(EtherCAT Slave Information)是联系主站和从站的桥梁,主站可以通过xml格式的从站设备描述文件识别从站的特征信息、获取对象字典信息、进行组态等。因此…

vue3(二)-基础入门之列表循环、数组变动检测、filter模糊查询、事件修饰符

一、列表循环 of 和 in 都是一样的效果 html代码&#xff1a; <div id"app"><ul><li v-for"item of datalist">{{ item }}</li></ul><ul><li v-for"item in dataobj">{{ item }}</li></u…

k8s安装步骤

环境&#xff1a; 操作系统&#xff1a;win10 虚拟机&#xff1a;VMware linux发行版&#xff1a;CentOS7.9 CentOS镜像&#xff1a;CentOS-7-x86_64-DVD-2009 master和node节点通信的ip(master)&#xff1a; 192.168.29.164 0.检查配置 本次搭建的集群共三个节点&#xff0c;…

【开发实践】使用POI实现导出带有复杂表头的的excel文件

一、需求分析 公司业务部门需要&#xff0c;根据一些数据&#xff0c;加上表头&#xff0c;导出需要的excel表格。效果如下&#xff1a; 二、代码实现 【依赖准备】 <!-- POI --><dependency><groupId>org.apache.poi</groupId><artifactId>po…