Flask 学习100-Flask-SocketIO 结合 xterm.js 实现网页版Xshell

前言

xterm.js 是一个使用 TypeScript 编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用。
可以实现 web-terminal 功能,类似于Xshell 操作服务器。
Flask-SocketIO 快速入门与使用基础参考前面这篇https://www.cnblogs.com/yoyoketang/p/18022139

前后端交互

前端代码
io.connect 连上服务端
socket.emit 给后端发送消息
socket.on('response', ) 监听服务端返回的数据

<!DOCTYPE html>
<html>
<head>
    <title>web</title>
    <meta charset="UTF-8">
    <script type="text/javascript" src="/static/jquery-3.5.1.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
</head>

<body>
<script type="text/javascript" charset="UTF-8">
    $(document).ready(function() {
        const namespace = '/ws'
        // 连上服务器
        const socket = io.connect("http://" + document.domain + ":" + location.port + namespace);

        // 发送message 消息
        socket.emit("message",{"data":"hello world!"});

        // 收到数据后,执行输出
        socket.on('response', function(recv) {
            console.log('hello: ' + recv.data)
        });
    });
</script>
</body>
</html>

flask 后端代码

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



app = Flask(import_name=__name__,
            static_url_path='/static',   # 配置静态文件的访问url前缀
            static_folder='static',      # 配置静态文件的文件夹
            template_folder='templates') # 配置模板文件的文件夹

app.config['SECRET_KEY'] = "abcdef"
socketio = SocketIO(app)


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


# 出现消息后,率先执行此处
@socketio.on("message", namespace="/ws")
def socket(message):
    print(f"接收到消息: {message['data']}")
    for i in range(1, 10):
        socketio.sleep(1)
        socketio.emit("response",           # 绑定通信
                      {"data": i},           # 返回socket数据
                      namespace="/ws")


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


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


if __name__ == '__main__':
    socketio.run(
        app,
        debug=True,
        host="0.0.0.0",
        port=8000,
        allow_unsafe_werkzeug=True
    )

基本代码调通后,再去实现一个WebSSH终端就会变得很容易, web-terminal终端我们需要xterm.js这个前端库来实现,

xterm.js 实现网页版终端

前端实现
io.connect 连上服务端
socket.emit 给后端发送消息
socket.on('response', ) 监听服务端返回的数据
term.onData 监听terminal终端上输入的数据

<!DOCTYPE html>
<html>
<head>
    <title>web</title>
    <meta charset="UTF-8">
    <script type="text/javascript" src="/static/jquery-3.5.1.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
    <link rel="stylesheet" href="/static/bootstrap.min.css">
    <!-- 引入 xterm 的 CSS 文件 -->
    <link rel="stylesheet" href="/static/xterm.css">
    <!-- 引入 xterm 的 JavaScript 文件 -->
    <script src="/static/xterm.js"></script>


</head>
<body>
    <div id="terminal"></div>
    <script>
        const window_width = $(window).width();
        const window_height = $(window).height();
        const term = new Terminal(
            {
                cols: Math.floor(window_width / 9),
                rows: Math.floor(window_height / 9),
                useStyle: false,
                convertEol: true, //启用时,光标将设置为下一行的开头
                cursorBlink: true,
                // disableStdin: false, //是否应禁用输入。
                cursorStyle: null, //光标样式
                // theme: {
                //      foreground: 'yellow', //字体
                //      background: '#060101', //背景色
                //      cursor: 'help'//设置光标
                //  }
            }
        )
        console.log("高度" + window_height + "宽度" + window_width);

        $(document).ready(function() {
            // 连接服务器
            const namespace = '/ws';
            const socket = io.connect("http://" + document.domain + ":" + location.port + namespace);

            socket.on("connect", function(){
                // 打开终端
                term.open(document.getElementById('terminal'));
            });

            // 接受后端数据,并写到控制台
            socket.on("response", function(recv){
                term.write(recv.data);
            });

            // terminal终端上输入的数据发送消息到服务端
            term.onData((data) => {
              console.log('发送消息:'+data)
              socket.send(data);
              // socket.emit('message', {"data":data});
            });
        });
</script>
</body>
</html>

这里有个坑,之前看到其他人写的是term.on, 是比较老的xterm.js语法

           // 发送消息到对端
            term.on("data",function(data){
               socket.send(data);
               //socket.emit("message",{"data":data});
            });

最新的xterm.js语法改成term.onData了

            // terminal终端上输入的数据发送消息到服务端
            term.onData((data) => {
              console.log('发送消息:'+data)
              socket.send(data);
              // socket.emit('message', {"data":data});
            });

flask 后端代码实现

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

app = Flask(import_name=__name__,
            static_url_path='/static',    # 配置静态文件的访问url前缀
            static_folder='static',       # 配置静态文件的文件夹
            template_folder='templates')  # 配置模板文件的文件夹

app.config['SECRET_KEY'] = "abcdef"
socketio = SocketIO(app)


def ssh_cmd():
    tran = paramiko.Transport(('192.1.1.x', 22,))
    tran.start_client()
    tran.auth_password('root', 'aa*******')
    chan = tran.open_session()
    chan.get_pty(height=492, width=1312)
    chan.invoke_shell()
    return chan


sessions = ssh_cmd()


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


@socketio.on("message", namespace="/ws")
def socket(message):
    """接收到前端message消息,执行此方法"""
    print(f"接收到消息: {message}")
    sessions.send(message)  # 传到服务器上执行
    ret = sessions.recv(4096)
    socketio.emit(
        "response",
        {"data": ret.decode("utf-8")},
        namespace="/ws"
    )


@socketio.on("connect", namespace="/ws")
def connect():
    """当websocket连接成功时,自动触发connect默认方法"""
    ret = sessions.recv(4096)
    socketio.emit(
        "response",
        {"data": ret.decode("utf-8")},
        namespace="/ws")
    print("链接建立成功..")


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


if __name__ == '__main__':
    socketio.run(
        app,
        debug=True,
        host="0.0.0.0",
        port=8000,
        allow_unsafe_werkzeug=True
    )

浏览器打开web网页地址就能看到连接服务器成功

于是一个简单的网页版xshell就可以实现了

参考学习相关博客
Flask 框架:运用SocketIO实现WebSSH https://www.bmabk.com/index.php/post/159498.html
用 xterm.js 实现一个简易的 web-terminal ! https://juejin.cn/post/6918911964009725959

posted @ 2024-02-20 20:48  上海-悠悠  阅读(445)  评论(0编辑  收藏  举报