先講一下簡單的原理:前端和後端的通訊,使用的是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窗口,這些窗口不存在信息的相互影響。css
在收到前端的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.jsnode
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.jsxreact
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 = null; this.term2 = 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> } } export default NetWorkConfig
XtermTest.jsxgit
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工程。目前仍是一個比較粗糙的版本。效果如圖:點擊按鈕1的時候初始化第一個窗口,點擊按鈕2的時候初始化第二個窗口。由於傳遞了不一樣的msgId,兩個窗口不會有信息的干擾。github