Xterm是一個(gè)基于X Window System的終端仿真器(Terminal Emulator)。Xterm最初由MIT開發(fā),它允許用戶在X Window環(huán)境下運(yùn)行文本終端程序。Xterm提供了一個(gè)圖形界面終端,使用戶能夠在圖形桌面環(huán)境中運(yùn)行命令行程序。而xterm.js是一個(gè)用于在瀏覽器中實(shí)現(xiàn)終端仿真的JavaScript庫。它允許在Web頁面中創(chuàng)建交互式的終端界面,用戶可以在瀏覽器中運(yùn)行命令行程序,執(zhí)行命令,并與終端進(jìn)行交互。
主要特點(diǎn)和功能包括:
- 終端仿真: xterm.js通過JavaScript模擬了一個(gè)終端環(huán)境,支持常見的終端功能,包括光標(biāo)移動(dòng)、顏色控制、滾動(dòng)等。
- 多平臺(tái)支持: 由于是基于JavaScript實(shí)現(xiàn),xterm.js可以在各種現(xiàn)代瀏覽器上運(yùn)行,無論是在桌面還是移動(dòng)設(shè)備上。
- 自定義外觀: xterm.js提供了豐富的配置選項(xiàng),用戶可以定制終端的外觀和行為,包括顏色、字體、光標(biāo)樣式等。
- 剪貼板支持: 支持從終端復(fù)制文本到剪貼板,并從剪貼板粘貼文本到終端。
- WebSockets和其他集成: 可以與WebSockets等通信協(xié)議集成,以便在瀏覽器中實(shí)現(xiàn)實(shí)時(shí)的終端交互。
- 支持Unicode和UTF-8: 能夠正確顯示和處理Unicode字符,支持UTF-8編碼。
xterm.js通常被用于Web應(yīng)用程序中,尤其是在需要提供命令行界面的場景下,如在線終端、遠(yuǎn)程服務(wù)器管理等。這使得開發(fā)者能夠在瀏覽器中實(shí)現(xiàn)類似于本地終端的交互體驗(yàn),而無需使用本地終端模擬器。
AJAX 實(shí)現(xiàn)Web交互
AJAX(Asynchronous JavaScript and XML)是一種用于在Web應(yīng)用程序中實(shí)現(xiàn)異步數(shù)據(jù)交換的技術(shù)。它允許在不重新加載整個(gè)頁面的情況下,通過在后臺(tái)與服務(wù)器進(jìn)行小規(guī)模的數(shù)據(jù)交換,實(shí)現(xiàn)動(dòng)態(tài)更新網(wǎng)頁內(nèi)容的目的。AJAX廣泛用于創(chuàng)建交互性強(qiáng)、用戶體驗(yàn)良好的Web應(yīng)用程序,例如在加載新數(shù)據(jù)、進(jìn)行表單驗(yàn)證、實(shí)現(xiàn)自動(dòng)完成搜索等方面。
如下前端部分,通過使用ajax
向后端提交數(shù)據(jù),當(dāng)success:function
接收到數(shù)據(jù)后直接將數(shù)據(jù)動(dòng)態(tài)回寫到Xterm
終端上,代碼如下所示;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" />
<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>
<!--提交數(shù)據(jù)-->
<div id="terminal"></div>
<input type="text" id="address" placeholder="主機(jī)地址"/>
<input type="text" id="command" placeholder="執(zhí)行命令"/>
<input type="button" value="執(zhí)行命令" onclick="show()">
</div>
</body>
</html>
后端部分的實(shí)現(xiàn)很簡單,首先封裝一個(gè)ssh_shell
用于執(zhí)行命令,用戶傳入數(shù)據(jù)后,直接執(zhí)行并將返回結(jié)果放入到ref內(nèi)即可。
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":
# 接收數(shù)據(jù)
json_value = request.get_json()
ref = ssh_shell("192.168.150.128","root","123123","22",json_value["command"])
# 發(fā)送數(shù)據(jù)
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實(shí)現(xiàn)Web終端
繼續(xù)擴(kuò)展將編輯框去掉,用戶輸入數(shù)據(jù)后直接傳入到Xterm內(nèi),Xterm里賣弄判斷如果出現(xiàn)了回車,則像后端發(fā)送ajax數(shù)據(jù),否則繼續(xù)偵聽并記下輸入數(shù)據(jù)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" />
<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, //光標(biāo)閃爍
cursorStyle: "underline", //光標(biāo)樣式
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);
// 如果按下回車,則發(fā)送命令,并等待輸出結(jié)果
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>
后端收到數(shù)據(jù)后解析命令,比對命令是否存在,根據(jù)不同的命令執(zhí)行不同的分支。
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":
# 接收數(shù)據(jù)
json_value = request.get_json()["command"]
if len(json_value) != 0:
# 判斷使用哪一個(gè)分支
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()
運(yùn)行后可輸出一個(gè)交互式WebShell環(huán)境,如下圖所示;
WebSocket 實(shí)現(xiàn)終端
雖然WebSSH可以方便管理主機(jī),但如果需要批量運(yùn)維則需要開發(fā)一個(gè)可以多條消息共同推送的命令行。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" />
<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);
// 如果按下回車,則發(fā)送命令,并等待輸出結(jié)果
if(code == 13)
{
// 發(fā)送數(shù)據(jù)到后端
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);
}
});
// 接受后臺(tái)返回并輸出
socket.on('response', function(recv)
{
console.log(recv.value);
term.write(recv.value);
});
});
</script>
</body>
</html>
后臺(tái)接收參數(shù),并更具不同的參數(shù)執(zhí)行不同的運(yùn)維函數(shù),此處只做演示,具體功能需要自行編寫。
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")
# 出現(xiàn)消息后,率先執(zhí)行此處
@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")
# 當(dāng)websocket連接成功時(shí),自動(dòng)觸發(fā)connect默認(rèn)方法
@socketio.on("connect",namespace="/Socket")
def connect():
print("鏈接建立成功..")
# 當(dāng)websocket連接失敗時(shí),自動(dòng)觸發(fā)disconnect默認(rèn)方法
@socketio.on("disconnect",namespace="/Socket")
def disconnect():
print("鏈接建立失敗..")
if __name__ == '__main__':
socketio.run(app,debug=True)
Socket版本的將會(huì)更流暢,如下圖所示;文章來源:http://www.zghlxwxcb.cn/news/detail-747348.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-747348.html
到了這里,關(guān)于Flask 運(yùn)用Xterm實(shí)現(xiàn)交互終端的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!