您现在的位置是:首页 >技术教程 >WebTerminal功能实现与代码演示(基于Golang和Xterm.js)网站首页技术教程

WebTerminal功能实现与代码演示(基于Golang和Xterm.js)

Naisu Xu 2024-06-17 11:28:13
简介WebTerminal功能实现与代码演示(基于Golang和Xterm.js)

目的

WebTerminal是一个比较有意思的功能,让我们可以脱离专门的软件,在浏览器中就可以与Linux设备进行交互。这篇文章将对这个功能做个简单的说明与演示。

例程地址如下:
https://github.com/NaisuXu/web-terminal-demo-with-golang-and-xterm

方案说说明

WebTerminal这个市面上叫法还是挺混乱的,大多数情况下还和WebSSH混在一起,两者本质上是有一些区别的。这里也把这两者混在一起列举下常见的几个方案:

在这里插入图片描述
上面这个情况常见都是用在运维管理等使用,可以在浏览器中管理多台设备或服务器。

在这里插入图片描述
上面这个情况也常用在运维管理等使用。

在这里插入图片描述
上面这个情况主要用于设备本身通过Web UI进行操作,想要更加高级的操作时可以直接通过终端进行。

这篇文章实现的是最后一种方式。另外因为我主要用在嵌入式Linux设备中,Web Server评估下来使用Golang是适应性和开发效率综合来说最好的。

实现过程与代码演示

前端页面(Xterm.js)

前端实现Terminal功能主要使用 Xterm.js 这个库,官方页面如下:
https://xtermjs.org/

VS Code中的中终端窗口就是使用这个库的,其中后端使用 node-pty

安装了Node.js的情况下新建项目目录并进入,然后初始化项目并下载xterm库:

npm init -y
npm install xterm

新建 index.html 文件,文件内容如下:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="node_modules/xterm/css/xterm.css" />
    <script src="node_modules/xterm/lib/xterm.js"></script>
</head>
<body>
    <div id="terminal"></div>
    <script>
        const term = new Terminal();
        term.open(document.querySelector('#terminal'));
    </script>
</body>
</html>

这时候就可以测试 Xterm.js 的功能了,可以使用Terminal对象的 write 方法向终端窗口中输出内容,使用 onData 方法可以接收终端窗口中的键盘操作:
在这里插入图片描述

上面就是前端页面最核心的东西了,接下来只要处理与后端服务的数据交互即可。这里使用WebSocket方式,完成后的代码如下:

<!DOCTYPE html>
<html>

<head>
    <title>WebTerminal</title>
    <link rel="stylesheet" href="./node_modules/xterm/css/xterm.css" />
    <script src="./node_modules/xterm/lib/xterm.js"></script>
</head>

<body>
    <div style="width: 736px; height: 408px;">
        <!-- 目前版本的 Xterm 5.1.0 默认串口大小 24x80 -->
        <div id="terminal"></div>
    </div>
    <script>
        const term = new Terminal();

        term.open(document.querySelector('#terminal')); // 挂载

        const socket = new WebSocket(`ws://${window.location.host}/webterminal`); // 创建WebSocket连接

        term.onData((data) => { // 网页xterm窗口中有输入的数据
            // console.log('term.onData:', data);
            socket.send(data); // 通过WebSocket发送给服务器
        });
        
        socket.onmessage = (event) => { // 收到来自服务器的WebSocket消息
            // console.log('socket.onmessage:', event.data);
            term.write(event.data); // 向xterm对象写入数据
        };
    </script>
</body>

</html>

后端服务(Golang)

安装了Go的环境下,在刚才同目录中,使用下面命令初始化项目:

go mod init webterminal

新建 main.go 文件,文件内容如下:

package main

import (
	"embed"
	"net/http"
	"os/exec"

	"github.com/creack/pty"
	"github.com/olahol/melody"
)

//go:embed index.html node_modules/xterm/css/xterm.css node_modules/xterm/lib/xterm.js
var content embed.FS

func main() {
	c := exec.Command("sh") // 系统默认shell交互程序
	f, err := pty.Start(c)  // pty用于调用系统自带的虚拟终端
	if err != nil {
		panic(err)
	}

	m := melody.New() // melody用于实现WebSocket功能

	go func() { // 处理来自虚拟终端的消息
		for {
			buf := make([]byte, 1024)
			read, err := f.Read(buf)
			if err != nil {
				return
			}
			// fmt.Println("f.Read: ", string(buf[:read]))
			m.Broadcast(buf[:read]) // 将数据发送给网页
		}
	}()

	m.HandleMessage(func(s *melody.Session, msg []byte) { // 处理来自WebSocket的消息
		// fmt.Println("m.HandleMessage: ", string(msg))
		f.Write(msg) // 将消息写到虚拟终端
	})

	http.HandleFunc("/webterminal", func(w http.ResponseWriter, r *http.Request) {
		m.HandleRequest(w, r) // 访问 /webterminal 时将转交给melody处理
	})

	fs := http.FileServer(http.FS(content))
	http.Handle("/", http.StripPrefix("/", fs)) // 设置静态文件服务

	http.ListenAndServe("0.0.0.0:22333", nil) // 启动服务器,访问 http://本机(服务器)IP地址:22333/ 进行测试
}

代码比较简单,核心功能就是调用系统中的虚拟终端,然后通过WebSocket和网页进行双向通讯。

上面代码只是用于功能测试使用的,这个代码有个问题是并没有对每个客户端进行单独处理,所以打开多个网页时操作都会同步响应。

使用下面命令安装相关依赖:

go mod tidy

编译与测试

在项目目录下创建 build.sh 文件,文件内容如下:

#!/bin/sh
GOOS=linux GOARCH=amd64 go build -o webterminal_linux_x86-64
GOOS=linux GOARCH=arm GOARM=7 go build -o webterminal_linux_armv7
GOOS=linux GOARCH=arm GOARM=5 go build -o webterminal_linux_armv5

后面三行命令分别用于编译生成三个平台的程序,你也可以根据需要来编写。

使用下面命令进行编译(注意window中需要使用 git-bash 等操作):

# chmod +x ./build.sh
./build.sh

将编译生成的程序拷贝到对应的平台中就可以进行测试了。

linux_x86-64 (Ununtu 22.04 AMD Ryzen 5 PRO 4650U):
在这里插入图片描述

linux_armv5 (NUC980 Linux buildroot 5.10.103+ armv5tejl GNU/Linux):
在这里插入图片描述

这里没有测试 linux_armv7 平台,有条件的话可以找一个树莓派(4B)进行测试。

总结

基于Golang和Xterm.js实现WebTerminal功能原理上比较简单,实际应用时更多的是需要根据需求进行功能和使用上的优化。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。