- 文档
- 教程
- 后端
【免费下载链接】CodeGuide
:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!
WaLiSSH 是一款本地运行的 AI SSH 运维助手,采用 Tauri 桌面应用 + Spring Boot DDD 后端服务架构。本节(第1-7节)承接上一节的 SSH 连接 API 对接成果,解决"连接上服务器之后终端面板如何盘活"的核心问题——实现打开终端会话、输入命令、读取输出、调整窗口大小、断连检测与重连等完整交互能力。读完本文,你将掌握一条从前端 xterm.js 键盘事件到后端 JSch Shell 通道的全链路数据流设计,以及为什么在 Tauri 本地场景下用 HTTP 轮询替代 WebSocket 的工程取舍。
一、本章诉求:终端面板从"空壳"到"可用"
上一节完成了前端和后端 SSH 连接 API 的对接,可以在界面上完成创建、编辑、删除、连接、断开 SSH 服务器等操作。但连接上服务器之后,终端面板还只是一个空壳——没办法输入命令,也看不到输出。
本节要实现 SSH 终端的核心交互能力:
- 打开终端会话(进入可输入、可输出的交互状态);
- 输入命令(键盘字符写入远程 Shell);
- 读取输出(远程执行结果回传渲染);
- 调整窗口大小(终端列宽行高同步到远程 Pty);
- 断连检测与重连(会话异常时的兜底能力)。
整体实现横跨两端:
- 后端:终端会话管理,落在 DDD 架构的 Domain + Infrastructure 两层;
- 前端:xterm.js 集成,改造 TerminalPanel 组件。
承接产品需求的角度看,这就是一次典型的"用户说我要在 Web 终端里敲命令"的需求落地:你需要设计从后端 Shell 通道到前端渲染的完整链路。
二、流程设计:输入/输出两条数据路径
从前端 xterm.js 到后端 JSch Shell 通道,完整的数据流被拆分成两条相对独立的路径:输入路径和输出路径。
1. 输入路径:键盘 → POST /terminal/write → JSch OutputStream
用户敲键盘时,字符从前端一路流向远程 Shell:
用户在 xterm.js 里敲键盘 → onData 事件拿到输入字符 → 写入缓冲(10ms 批量合并) → 调用 POST /terminal/write 发到后端 → 后端写入 JSch Shell 的 OutputStream → 远程 Shell 收到字符并执行这里有两个值得注意的工程细节:
- onData 事件:xterm.js 通过
onData把用户每次按键的字符(包括控制字符、退格、回车等)抛给应用层,这是前端集成 xterm.js 的标准入口; - 10ms 批量合并:高频按键会产生大量小请求,前端并不每键必发,而是把 10ms 窗口内的输入字符合并成一批再通过
POST /terminal/write发送,显著降低请求数量,避免本地 HTTP 接口被打爆。
2. 输出路径:JSch InputStream → 后台线程 → GET /terminal/read 轮询 → terminal.write()
远程命令产生的输出,通过另一条路径回传渲染:
远程 Shell 产生输出 → 后台读取线程持续从 JSch InputStream 读取 → 写入 StringBuilder 缓冲区 → 前端每 50ms 调用 GET /terminal/read 轮询 → 拿到输出后 terminal.write() 渲染到 xterm.js输出路径的三个关键设计:
- 后台读取线程:从 JSch
InputStream读取是阻塞式的,必须放在独立线程里持续消费,不能占用 HTTP 请求线程; - StringBuilder 缓冲:读取线程把累积输出写入共享缓冲区,作为"生产者";前端轮询请求作为"消费者"取走内容,实现读写解耦;
- 50ms 轮询渲染:前端定时通过
GET /terminal/read拉取新输出,再调用 xterm.js 的terminal.write()增量渲染,保证终端画面持续刷新。
3. 核心接口一览
| 接口 | 方向 | 作用 | 频率 |
|---|---|---|---|
POST /terminal/write | 前端 → 后端 | 写入输入字符(10ms 批量合并) | 随按键触发 |
GET /terminal/read | 前端 ← 后端 | 轮询拉取远端输出 | 每 50ms |
这两个接口连同终端会话的建立、窗口 resize、断开与重连,共同构成后端终端会话管理的能力面。
三、为什么不用 WebSocket:Tauri 本地场景下的工程取舍
这是本节最值得思考的设计决策之一:终端双向通信没有采用 WebSocket,而是用 HTTP 轮询。
核心原因要回到 WaLiSSH 的产品形态:
- WaLiSSH 是 Tauri 桌面应用,前端(walissh-client)与后端(walissh-server)都运行在本地;
- 前后端之间的网络延迟极低,50ms 的轮询间隔对终端操作来说完全够用;
- 相比维护一条长连接(WebSocket 的连接管理、心跳、断线重连、消息帧协议),HTTP 轮询实现更简单、更易排查。
从工程成本角度看,轮询方案把"连接状态"问题简化成了"每次请求独立"的问题,配合后端会话的断连检测与重连机制,已经可以覆盖 Tauri 本地场景下的终端交互需求。这个取舍也说明:技术选型没有银弹,方案要匹配部署环境。
四、后端实现:基于 DDD 的终端会话管理
1. 技术底座:JSch——纯 Java 的 SSH2 客户端库
终端会话的底层通道建立在 JSch 之上。JSch(Java Secure Channel)是 SSH2 协议的纯 Java 实现,提供完整的 SSH 客户端能力:
- 建立 SSH 会话(
Session); - 执行远程命令(
ChannelExec); - 创建 SFTP 通道(
ChannelSftp); - 端口转发、X11 转发等。
WaLiSSH 主要用到前三项能力,其中本节的 Shell 通道会话即基于Session建立。认证方式支持密码认证与密钥认证两种,互斥使用——优先使用密钥,没有密钥则走密码(详见 第1-3节:SSH连接云服务器)。
2. 分层落位:Domain 层定义能力,Infrastructure 层实现通道
本节的后端部分遵循 DDD 架构,落在 Domain + Infrastructure 两层,这与前序章节的领域设计一脉相承:
- Domain 层:定义终端会话管理的领域服务接口(如打开会话、写入输入、读取输出、调整窗口、断连检测与重连),以及会话相关的实体与值对象。按照 DDD 的设计理念,领域层只描述业务规则,不关心底层通道细节;
- Infrastructure 层:提供 Port 接口的适配实现,真正操作 JSch 的
OutputStream/InputStream,负责后台读取线程的启动与生命周期管理。
这种"接口在领域层、实现在基础设施层"的依赖倒置(DIP)设计,是 第1-5节:连接SSH简单领域设计 中反复强调的核心思想——以后无论底层换成什么 SSH 库,Domain 层代码一行都不用改。
3. 接口出口:SshConnectionController 暴露能力面
上一节(第1-6节:UI接口对接SSH连接操作)已经建立了SshConnectionController接口层,把创建连接、更新连接、删除连接、获取连接、连接列表等 SSH 连接管理能力通过 HTTP 暴露出去。本节的POST /terminal/write、GET /terminal/read以及窗口 resize、断连重连等接口,正是沿着同样的"Controller → 领域服务 → Port 适配器 → JSch 通道"链路追加的终端会话能力面。
4. 为 AI Agent 预留:terminalSessionId 的衔接
终端会话不是一个孤立功能,它要为后续的 AI Shell 智能体能力服务。在 WaLiSSH 第2部分(第2-2节:最小MVP版本,UI对接)可以看到:SshExecuteAdkTool会从 ThreadLocal(或会话级变量)拿到terminalSessionId,通过TerminalSessionPort.executeCommandAndWait写入命令并阻塞等待 Shell 输出完成,再返回清理后的结果给 LLM。这说明本节的终端会话管理需要对外暴露terminalSessionId这样的会话标识,供后续 AI 工具链注入与复用;第2-3节:ReAct Case 架构设计与实现 中用户提交的ChatRequestDTO也携带了terminalSessionId字段。因此在设计终端会话时,把会话标识、会话生命周期管理做好,就等于为 AI Agent 的命令执行能力铺好了路。
五、前端实现:xterm.js TerminalPanel 集成
前端部分以 xterm.js 为终端渲染核心,改造TerminalPanel组件,接入两条数据路径:
- 输入侧:监听 xterm.js 的
onData事件拿到用户输入字符 → 10ms 批量合并 → 调POST /terminal/write; - 输出侧:定时(50ms)调
GET /terminal/read拉取输出 →terminal.write()渲染。
前端 API 对接方式与上一节的api/request.ts保持一致:前端会把一系列 API 操作汇总到类文件/包文件下,以面向对象结构组织,与 Java 代码结构类似,后端开发同学阅读这部分 TypeScript 代码也不会吃力。这类封装统一了请求入口、错误处理与数据模型,方便 TerminalPanel 只关心"取到什么输出、往哪里渲染"。
六、验证与小结
本节的成果可以这样验证:在 WaLiSSH 中连接一台云服务器后,进入终端面板,输入ls、top等命令,能够看到远程 Shell 的实时输出与本地 xterm.js 渲染一致;调整终端窗口大小后,远程 Pty 的尺寸随之同步;断开网络或服务器侧关闭会话时,能够触发断连检测并支持重连。
回顾本节的技术要点:
- 双通道数据流:输入走
POST /terminal/write(10ms 批量合并),输出走GET /terminal/read(50ms 轮询),读写解耦、缓冲分离; - HTTP 轮询替代 WebSocket:基于 Tauri 本地部署、低延迟的场景做出的工程取舍,实现更简单、足够可用;
- DDD 分层承载:终端会话管理能力定义在 Domain 层,JSch 通道实现在 Infrastructure 层,接口层通过 Controller 暴露;
- 为 AI Agent 预留会话标识:
terminalSessionId贯穿后续 AI 章节的SshExecuteAdkTool与ChatRequestDTO,终端会话是智能体命令执行能力的地基。
到这里,WaLiSSH 已经拥有了一块"活"的 SSH 终端面板。下一步(第2部分)将在这条 Shell 通道之上引入 Tool 与智能体编排,构建最小 MVP 版本的 AI Shell 运维助手。
- 文档
- 教程
- 后端
【免费下载链接】CodeGuide
:books: 本代码库是作者小傅哥多年从事一线互联网 Java 开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果本仓库能为您提供帮助,请给予支持(关注、点赞、分享)!
相关推荐
WaLiSSH 服务端 SSH 连接 API 与前端对接实战:从 SshConnectionController 到 api/request.ts 的完整链路
WaLiSSH 服务端 SSH 连接 API 与前端对接实战:从 SshConnectionController 到 api/request.ts 的完整链路
文档教程后端xterm.js输入处理机制:从键盘事件到终端命令的旅程
xterm.js输入处理机制:从键盘事件到终端命令的旅程 你是否曾好奇,当在网页终端中按下键盘时,字符是如何从按键变成终端中的命令的?本文将深入解析xterm.
前端UI组件终端与 Shell 原理详解:easy-vibe 命令行基础从零到实战
终端与 Shell 原理详解:easy vibe 命令行基础从零到实战 本文面向零基础读者,系统拆解终端(Terminal)、Shell 与内核(Kernel)
教程文档人工智能Vibe Coding
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考