实现网页版xshell,需要学习的东西有点多,下面一个个简单介绍
Xterm.js
这是网上的一个开源框架,其作用主要是界面方面,比如新建一个小黑窗,设置各种样式等作用,用法也很简单,具体可查https://xtermjs.org/
experss
express是一个开源的框架,大多数公司也在使用这样的框架作为Node中间层或者是服务端使用,可以快速搭建一个服务端出来,
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send(' Hello World! ');
});
app.listen(3000);
使用浏览器访问 http://localhost:3000 看到 Hello World! 表示启动成功
其可以设置各种访问路径,请求方式,以及融合其他开源框架
websocket
WebSocket 是 HTML5 开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。 WebSocket 通信协议于2011年被IETF定为标准RFC 6455,WebSocketAPI 被 W3C 定为标准。 在 WebSocket API 中,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
一旦建立连接(直到断开或者出错),服务端与客户端握手后则一直保持连接状态,是持久化连接;这个都比较熟悉不多说
ssh2
ssh2是专门为了连接服务器开发的开源框架,具体可看https://github.com/mscdex/ssh2
界面
代码
客户端代码
//首先调用客户端组件
<SSHClient host={params.host} username={params.username} password={params.password} port={params.port}/>
//SSHClient
import React, { useEffect, useState,FunctionComponent } from 'react';
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
type Props = {//四个参数传过来,这是你需要连接的服务器的信息。
host?:string;
port?:number;
username?:string;
password?:string;
}
const WebTerminal :FunctionComponent<Props> = (props) => {
const {host,port,username,password} = props
const [webTerminal, setWebTerminal] = useState<Terminal | null>(null);
const [ws, setWs] = useState<WebSocket | null>(null);
useEffect(() => {
// 新增监听事件
if (webTerminal && ws) {
// 监听
webTerminal.onKey(e => {
const { key } = e;
ws.send(key);
});
// ws监听
ws.onmessage = e => {
console.log(e);
if (webTerminal) {
if (typeof e.data === 'string') {
webTerminal.write(e.data);
} else {
console.error('格式错误');
}
}
};
}
}, [webTerminal, ws]);
useEffect(() => {
// 初始化终端
const ele = document.getElementById('terminal');
while(ele && ele.hasChildNodes()){ //当table下还存在子节点时 循环继续
//这里是为了参数输错的情况下,从新改正参数,点击连接,新建一个新的窗口
ele && ele.firstChild && ele.removeChild(ele.firstChild);
}
if (ele) {
// 初始化
const terminal = new Terminal({
cursorBlink: true,
cols: 175,
rows: 40,
});
terminal.focus();
terminal.onKey(e => {
// terminal.write(e.key);
if (e.key== '\r') {
// terminal.write('\nroot\x1b[33m$\x1b[0m');
} else if (e.key== '\x7F') {
terminal.write('\b \b');
}
});
terminal.open(ele);
terminal.write('连接中....');
setWebTerminal(terminal);
}
// 初始化ws连接
if (ws) ws.close();
const socket = new WebSocket('ws://127.0.0.1:3888');
socket.onopen = () => {//和服务端建立socket连接
let message = {
host:host,
port:port,
username:username,
password:password
};
socket.send(JSON.stringify(message));
};
setWs(socket);
}, [host,port,username,password]);
return <div id="terminal" />;
};
export default WebTerminal;
//直接跟随项目启动就行
服务端代码
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const SSHClient = require('ssh2').Client;
const utf8 = require('utf8');
const createNewServer = (machineConfig, socket) => {
const ssh = new SSHClient();
const { host, username, password,port } = machineConfig;
// 连接成功
ssh.on('ready', function () { //准备就绪,然后建立ssh连接
socket.send('\r\nSSH 连接成功 \r\n');
ssh.shell(function (err, stream) {
// 出错
if (err) {
return socket.send('\r\nSSH连接失败: ' + err.message + '\r\n');
}
// 前端发送消息
socket.on('message', function (data) {
stream.write(data);
});
// 通过sh发送消息给前端
stream.on('data', function (d) {
socket.send(utf8.decode(d.toString('binary')));
// 关闭连接
}).on('close', function () {
ssh.end();
});
})
// 关闭连接
}).on('close', function () {
socket.send('\r\nSSH连接关闭 \r\n');
// 连接错误
}).on('error', function (err) {
socket.send('\r\nSSH连接失败: ' + err.message);
// 连接
}).connect({
port,
host,
username,
password
});
}
const isJSON = (str) => { //判断是不是json,不然容易报错,这个服务就挂了
if (typeof str == 'string') {
try {
JSON.parse(str);
return true;
} catch(e) {
// console.log(str);
return false;
}
}
console.log('It is not a string!')
}
app.ws('/', (ws, req) => { //建立连接
ws.on("message", (data) => { //建立连接后获取客户端发过来的地址等信息
try {
isJSON(data) && createNewServer({
port: JSON.parse(data).port,
host: JSON.parse(data).host,
username: JSON.parse(data).username,
password: JSON.parse(data).password
}, ws)
} catch(e) {
console.log(e);
}
});
});
app.listen(3888,()=>{
console.log('3888 port is listening')
})
说明
- 这个服务端需要单独新建一个项目启动,不然是起不来的,启动以后先测试websocket连接通不通,通了的话,在测能不能链接上ssh服务器,服务端启动命令是node server.js(就是js文件名),客户端就是正常前端启动
- 这里客户端请求的连接是127.0.0.1,因为客户端和服务端我是在一个电脑上跑的,如果不是,就要换对应的