基于 xterm + websocket + vue 实现网页版终端 terminal

本文档详细介绍了如何使用xterm.js库结合WebSocket协议创建一个网页终端。前端通过安装xterm.js及其相关插件,配置WebSocket连接,并设置终端参数。在后端,数据通过WebSocket双向通信,实现命令的执行和显示。注意处理删除键的特殊情况,确保前端不进行额外处理,仅将操作转发给后端。此外,提供了xterm.js的配置选项和一些关键API的使用说明。

网页版的终端,首选 xterm.js,其次是 k9s,和后端的通信采用 websocket 通信协议,这是一种可以在单个TCP连接上进行全双工通信的协议。如果需要websocket 带 token 发起连接可以参考这篇文章
网页终端实现效果如下:
在这里插入图片描述

如果想要达到这种效果,那就跟着接下来的步骤一步步操作。

准备工作

1. 安装xterm

yarn add xterm

2. 安装xterm-addon-fit

yarn add xterm-addon-fit

xterm.js的插件,使终端的尺寸适合包含元素。

3. 安装xterm-addon-attach

yarn add xterm-addon-attach

xterm.js的附加组件,用于附加到Web Socket

前端实现

模板部分:

<template>
    <div >
        <div ref="terminal" />
    </div>
</template>

业务逻辑部分:

<script>
import 'xterm/css/xterm.css'
import {
   
    Terminal } from 'xterm'
import {
   
    FitAddon } from 'xterm-addon-fit'
import {
   
    AttachAddon } from 'xterm-addon-attach'

export default {
   
   
  name: 'terminal',
  data() {
   
   
    return {
   
   
      term: null,
      socketUri: 'ws://127.0.0.1:8088/podname',
      socket: '',
      accessToken: 'token',
    }
  },
  mounted() {
   
   
    this.initTerm();
  },
  beforeDestroy() {
   
   
    this.socket && this.socket.close();
    this.term && this.term.dispose();
  },
  methods: {
   
   
    initTerm() {
   
   
      // 1.xterm终端初始化
      const term = new Terminal({
   
   
        rendererType: "canvas", //渲染类型
        rows: 40, //行数
        cols: 100, // 不指定行数,自动回车后光标从下一行开始
        convertEol: true, //启用时,光标将设置为下一行的开头
        // scrollback: 50, //终端中的回滚量
        disableStdin: false, //是否应禁用输入
        windowsMode: true, // 根据窗口换行
        cursorStyle: "underline", //光标样式
        cursorBlink: true, //光标闪烁
        theme: {
   
   
          foreground: "#ECECEC", //字体
          background: "#000000", //背景色
          cursor: "help", //设置光标
          lineHeight: 20,
        },
      });
      // 2.webSocket初始化
      if (this.socketUri === '') return;
      this.socket = new WebSocket(
<think>好的,我现在需要帮助用户实现一个基于WebSocketxterm.js、Spring Boot和Vue 3的Web终端,类似于XShell的功能。首先,我得理清楚整个架构和各个组件的职责。 用户提到的技术栈包括WebSocketxterm.js、Spring Boot和Vue 3。根据引用内容,用户可能已经参考了一些现有的实现,比如使用xterm-addon-fit和xterm-addon-attach插件,以及通过Docker部署的示例。我需要结合这些信息,给出一个分步的解决方案。 首先,后端部分。Spring Boot需要处理WebSocket连接,并执行Linux命令。用户可能需要建立一个WebSocket服务端,当客户端连接时,创建对应的PTY(伪终端)进程。这涉及到Java中处理终端输入输出流的问题。引用中提到使用xterm-addon-attach来附加WebSocket,所以后端必须正确配置WebSocket端点,并能够将前端的输入转发到PTY进程,同时将PTY的输出发送回前端。 然后是前端部分,Vue 3需要集成xterm.js。根据引用[1],安装xterm及其插件是关键步骤。需要创建终端实例,使用fit插件调整尺寸,attach插件处理WebSocket通信。此外,可能需要处理窗口大小变化时的自适应,确保终端显示正确。 接下来是前后端的通信协议。WebSocket传输的数据应该是实时、双向的。前端发送用户的键盘输入到后端,后端将命令执行结果返回。需要注意处理字符编码和流式数据传输,避免阻塞。 安全性也是一个考虑点。用户提到引用[2]中的Docker运行参数,包括挂载.ssh目录,可能涉及到SSH连接管理。可能需要实现用户认证,比如在WebSocket连接时验证Token,或者集成Spring Security。此外,权限控制需要确保用户只能执行被允许的命令,防止恶意操作。 另外,引用[3]提到了看板娘的实现,可能属于UI增强部分,但用户当前的问题主要关注终端功能,这部分可以暂时忽略。 需要检查各个步骤的细节。例如,在Spring Boot中如何创建PTY进程?可能需要使用ProcessBuilder或期待库来处理交互式终端。同时,处理WebSocket会话的生命周期,确保在连接关闭时终止对应的进程。 在前端xterm.js的配置是否正确?比如,是否启用了必要的插件,是否正确处理了WebSocket的打开、消息接收和关闭事件。此外,终端可能需要支持调整大小,这需要后端同步调整PTY的尺寸,涉及发送窗口大小事件。 可能的错误点包括:WebSocket连接失败,可能是CORS问题或路径配置错误;终端输出乱码,可能由于编码不一致;输入命令没有响应,可能是后端未正确读取输入流;终端大小不一致,需要前后端同步调整。 最后,测试整个流程:启动后端,前端连接,输入命令查看输出是否正确,测试多行命令、特殊字符(如Ctrl+C)的处理,以及窗口调整是否正常。 总结步骤:搭建Spring Boot WebSocket服务,创建PTY进程;Vue集成xterm.js并配置WebSocket连接;处理输入输出流和窗口调整;加入安全措施如认证和权限控制;测试和调试。</think>### 分步实现 Web终端连接 Linux 的解决方案 --- #### 一、技术架构概述 1. **前端**:使用 Vue 3 + xterm.js 实现终端界面和用户交互 2. **通信层**:WebSocket 实现实时双向数据传输 3. **后端**:Spring Boot 处理 WebSocket 请求,创建伪终端(PTY)执行命令 4. **安全控制**:Spring Security + Token 认证(可选) --- #### 二、后端实现(Spring Boot) ##### 1. 添加依赖(pom.xml) ```xml <!-- WebSocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <!-- 伪终端支持 --> <dependency> <groupId>org.pty4j</groupId> <artifactId>pty4j</artifactId> <version>0.12.13</version> </dependency> ``` ##### 2. WebSocket 配置类 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(terminalSocketHandler(), "/ws/terminal") .setAllowedOrigins("*"); } @Bean public WebSocketHandler terminalSocketHandler() { return new TerminalSocketHandler(); } } ``` ##### 3. 核心处理逻辑 ```java public class TerminalSocketHandler extends TextWebSocketHandler { private Process process; private OutputStream outputStream; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { // 创建伪终端进程 String[] cmd = { "/bin/bash", "-l" }; // Linux 环境 ProcessBuilder builder = new ProcessBuilder(cmd); process = builder.start(); outputStream = process.getOutputStream(); // 启动线程读取终端输出 new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { session.sendMessage(new TextMessage(line + "\r\n")); } } catch (IOException e) { e.printStackTrace(); } }).start(); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 将用户输入转发到终端进程 outputStream.write(message.getPayload().getBytes()); outputStream.flush(); } } ``` --- #### 三、前端实现Vue 3 + xterm.js) ##### 1. 安装依赖 ```bash npm install xterm xterm-addon-fit xterm-addon-web-links ``` ##### 2. 终端组件(Terminal.vue) ```vue <template> <div ref="terminalContainer" class="terminal"></div> </template> <script setup> import { onMounted, ref } from 'vue'; import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; import { WebLinksAddon } from 'xterm-addon-web-links'; const terminalContainer = ref(null); const socket = ref(null); onMounted(() => { // 初始化终端 const term = new Terminal({ cursorBlink: true, fontSize: 14, theme: { background: '#1e1e1e' } }); const fitAddon = new FitAddon(); term.loadAddon(fitAddon); term.loadAddon(new WebLinksAddon()); term.open(terminalContainer.value); fitAddon.fit(); // 连接 WebSocket socket.value = new WebSocket('ws://localhost:8080/ws/terminal'); socket.value.onmessage = (event) => { term.write(event.data); }; term.onData(data => { socket.value.send(data); }); // 窗口大小调整 window.addEventListener('resize', () => fitAddon.fit()); }); </script> ``` --- #### 四、安全增强(可选) 1. **Token 认证**:在 WebSocket 连接时携带 JWT ```javascript const token = localStorage.getItem('token'); new WebSocket(`ws://your-domain/ws/terminal?token=${token}`); ``` 2. **后端校验** ```java @Override public void afterConnectionEstablished(WebSocketSession session) { String token = session.getUri().getQuery().split("=")[1]; if (!validateToken(token)) { session.close(); } // ...原有逻辑 } ``` --- #### 五、部署与测试 1. **启动 Spring Boot** ```bash mvn spring-boot:run ``` 2. **前端访问** ```bash npm run dev ``` 访问 `http://localhost:3000` 输入 Linux 命令测试 --- #### 六、关键问题解决 1. **中文乱码**:设置终端编码格式 ```java // 后端启动进程时指定编码 ProcessBuilder builder = new ProcessBuilder(cmd); builder.environment().put("LANG", "en_US.UTF-8"); ``` 2. **窗口大小同步**:通过 `resize` 命令调整 ```java // 前端发送窗口尺寸 term.onResize(size => { socket.send(JSON.stringify({ type: 'resize', cols: size.cols, rows: size.rows })); }); // 后端处理调整 String resizeCmd = String.format("stty rows %d cols %d", rows, cols); outputStream.write(resizeCmd.getBytes()); ``` --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值