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