xterm是一个使用TypeScript编写的前端终端组件。
1、demo主要介绍
本文demo主要实现一个页面一个webssh窗口,前端部分主要利用xterm
通过原生ws
和后端通信,后端部分使用nodejs+utf8+ws+ssh2
。
2、前端实现
基于vue项目,前端主要依赖包:xterm xterm-addon-fit
,使用前请install。
<template>
<div id="xterm"/>
</template>
<script>
import 'xterm/css/xterm.css'
import { Terminal } from 'xterm';
import { FitAddon } from "xterm-addon-fit";
export default {
name: 'WebShell',
data() {
return {
socketURI: 'ws://localhost:2000/'
}
},
mounted() {
this.initSocket()
},
beforeDestroy() {
this.socket.close()
this.term && this.term.dispose()
},
methods: {
initTerm() {
let element = document.getElementById('xterm');
// 设置了cols或者fitAddon.fit(); 当一行字符超过80个过会替换现在的内容,否则换行
const term = new Terminal({
cursorBlink: true, // 关标闪烁
cursorStyle: "underline", // 光标样式 'block' | 'underline' | 'bar'
scrollback: 100, // 当行的滚动超过初始值时保留的行视窗,越大回滚能看的内容越多,
});
this.term = term;
const fitAddon = new FitAddon();
this.term.loadAddon(fitAddon);
this.fitAddon = fitAddon;
term.open(element);
// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸),初始化的时候宽高都是对的
fitAddon.fit();
term.focus();
this.term.onData(data => {
this.socket.send(data);
});
window.addEventListener('resize', this.resizeTerm);
},
getColsAndRows(element) {
// 暂时不用
element = element || document.getElementById('xterm');
return {
rows: parseInt((element.clientHeight - 0) / 18),
cols: 10 // parseInt(element.clientWidth / 8)
};
},
resizeTerm() {
this.fitAddon.fit();
this.term.scrollToBottom();
},
initSocket() {
this.socket = new WebSocket(this.socketURI);
this.socketOnClose();
this.socketOnOpen();
this.socketOnError();
this.socketOnMessage();
},
socketOnOpen() {
this.socket.onopen = () => {
// 连接成功后
this.initTerm()
}
},
socketOnMessage() {
this.socket.onmessage = (event) => {
// 接收推送的消息
this.term.write(event.data.toString());
}
},
socketOnClose() {
this.socket.onclose = () => {
console.log('close socket')
}
},
socketOnError() {
this.socket.onerror = () => {
console.log('socket error')
}
}
}
}
</script>
<style scoped>
#xterm {
padding: 15px 0;
}
</style>
3、后端实现
前端主要依赖包:utf8 ssh2 ws
,使用前请install。
ssh2
用来实现nodejs和服务器进行连接和通信。
utf8
用来实现服务器返回的命令执行结果解码。
ws
用来实现后端和前端ws全双工通信。
/**用来实现单个webssh功能**/
const utf8 = require("utf8");
const SSHClient = require("ssh2").Client;
const Server = require("ws").Server;
const wss = new Server({
port: 2000
});
const serverInfo = {
host: '192.168.18.141',
port: 22,
username: 'root',
password: '密码不告诉你.'
};
function createSocket(ws) {
const ssh = new SSHClient();
ssh
.on("ready", function () {
ws.send("\r\n*** SSH CONNECTION ESTABLISHED ***\r\n");
ssh.shell(function (err, stream) {
if (err) {
return ws.emit("\r\n*** SSH SHELL ERROR: " + err.message + " ***\r\n");
}
ws.on("message", function (data) {
stream.write(data);
});
ws.on("close", function () {
console.log("close websocket。");
ssh.end();
});
stream
.on("data", function (d) {
let data = utf8.decode(d.toString("binary"));
ws.send(data);
})
.on("close", function () {
ssh.end();
});
});
})
.on("close", function () {
ws.close();
})
.on("error", function (err) {
console.log("\r\n*** SSH CONNECTION ERROR: " + err.message + " ***\r\n");
ws.close();
})
.connect(serverInfo);
}
wss.on("connection", function (ws) {
createSocket(ws);
});
4、遗留问题
1、浏览器resize后,webshell窗口宽高自适应、命令显示的问题;
2、设置了cols或者fitAddon.fit(); 当一行字符超过80个后,会替换现在的内容问题。