概述
先講一下簡單的原理:前端和後端的通信,使用的是socket.js,後端連接服務器,使用的是ssh2.js,頁面顯示出控制檯這個操作頁面,使用的是xterm.js。整個工作流程就是:前端在xterm.js裏面輸入文字,通過socket和後端通信,後端把前端傳過來的命令,通過ssh2連接服務器,得到服務器返回的數據,通過socket傳給前端,前端再顯示出socket返回的內容。所以這裏貼一下幾個官網,可以先了解一下。
https://socket.io/
https://github.com/staltz/xst...
https://github.com/mscdex/ssh2
再講一下需求:我這裏要創建一個服務端,能夠支持從前端得到要連接的服務器信息,再去創建ssh2連接,並且在頁面上能夠通過點擊按鈕等方式,創建多個xterm窗口。
服務端代碼
在收到前端的createNewServer信息時,會創建一個新的ssh連接。爲了區分不同的服務器窗口,前端必須傳遞一個msgId,用於給後端發送消息。然後前端就能夠監聽不同的msgId,將socket傳遞過來的信息顯示到不同的xterm窗口上。
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var utf8 = require('utf8');
var SSHClient = require('ssh2').Client;
function createNewServer(machineConfig, socket) {
var ssh = new SSHClient();
let {msgId, ip, username, password} = machineConfig;
ssh.on('ready', function () {
socket.emit(msgId, '\r\n***' + ip + ' SSH CONNECTION ESTABLISHED ***\r\n');
ssh.shell(function(err, stream) {
if(err) {
return socket.emit(msgId, '\r\n*** SSH SHELL ERROR: ' + err.message + ' ***\r\n');
}
socket.on(msgId, function (data) {
stream.write(data);
});
stream.on('data', function (d) {
socket.emit(msgId, utf8.decode(d.toString('binary')));
}).on('close', function () {
ssh.end();
});
})
}).on('close', function () {
socket.emit(msgId, '\r\n*** SSH CONNECTION CLOSED ***\r\n');
}).on('error', function (err) {
console.log(err);
socket.emit(msgId, '\r\n*** SSH CONNECTION ERROR: ' + err.message + ' ***\r\n');
}).connect({
host: ip,
port: 22,
username: username,
password: password
});
}
io.on('connection', function(socket) {
socket.on('createNewServer', function(machineConfig) {//新建一個ssh連接
console.log("createNewServer")
createNewServer(machineConfig, socket);
})
socket.on('disconnect', function(){
console.log('user disconnected');
});
})
http.listen(8000, function() {
console.log('listening on * 8000');
})
前端代碼
前端主要是先打開socket.io連接,在點擊創建按鈕的時候,把服務器的信息和msgId傳遞給後臺,讓後臺能夠創建一個新的ssh連接,然後在xterm窗口輸入數據的時候,把數據發送給服務端,並且監聽服務器返回的消息顯示到界面上來。
App.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import NetWorkConfig from "./NetWorkConfig"
import "../node_modules/xterm/dist/xterm.css"
import 'antd/dist/antd.css';
class App extends Component {
render() {
return (
<div className="App">
<NetWorkConfig/>
</div>
);
}
}
export default App;
NetWorkConfig.jsx
import React from "react"
import openSocket from 'socket.io-client';
import {Button} from "antd"
import XtermTest from "./XtermTest"
const socket = openSocket('http://localhost:8000');
class NetWorkConfig extends React.Component {
constructor(props) {
super(props);
this.createServer1 = this.createServer1.bind(this);
this.createServer2 = this.createServer2.bind(this);
this.term1 = {
term: null
}
this.term2 = {
term: null
}
}
createServer1() {
socket.emit("createNewServer", {msgId: 'net1', ip: "192.168.79.100", username: "lss", password: "PassW0rd"});
let term = this.term1.getTerm();
term.on("data", function(data) {
socket.emit('net1', data);
})
socket.on("net1", function (data) {
console.log(data)
term.write(data)
})
}
createServer2() {
socket.emit("createNewServer", {msgId: 'net2', ip: "192.168.79.100", username: "lss", password: "PassW0rd"});
let term = this.term2.getTerm();
term.on("data", function(data) {
socket.emit('net2', data);
})
socket.on("net2", function (data) {
term.write(data)
})
}
render() {
return <div>
<Button onClick={this.createServer1}>按鈕1</Button>
<Button onClick={this.createServer2}>按鈕2</Button>
<XtermTest ref={(term1) => {this.term1 = term1}} id="net1"/>
<XtermTest ref={(term2) => {this.term2 = term2}} id="net2"/>
</div>
}
componentDidMount() {
}
}
export default NetWorkConfig
XtermTest.jsx
import React from "react"
import { Terminal } from 'xterm';
import * as fit from '../node_modules/xterm/dist/addons/fit/fit';
class XtermTest extends React.Component {
constructor(props) {
super(props)
this.getTerm = this.getTerm.bind(this);
}
render() {
return <div id={this.props.id}></div>
}
getTerm() {
return this.term;
}
componentDidMount() {
Terminal.applyAddon(fit);
let {id} = this.props;
let terminalContainer = document.getElementById(id);
this.term = new Terminal({cursorBlink: true});
this.term.open(terminalContainer);
this.term.fit();
}
}
export default XtermTest
啓動說明
先node啓動server.js,然後再正常啓動react工程。目前還是一個比較粗糙的版本。