websocket 实现 web 远程终端需前后端协同:前端用 xterm.js 渲染终端并监听输入,通过 websocket 实时双向通信;后端用 node-pty 创建伪终端,桥接 stdin/stdout 与 websocket 数据流,并做好权限控制、会话隔离、尺寸同步和编码统一。

用 WebSocket 实现一个简单的 Web 远程终端,核心在于建立浏览器与后端的实时双向通道,并把终端输入输出“搬”到网页上。它不依赖云中转,也不需要额外客户端,只要前后端配合好,就能在浏览器里敲命令、看回显——就像本地 Terminal 一样自然。
前端:用 xterm.js 模拟终端界面
xterm.js 是目前最成熟、轻量且兼容性好的终端模拟库,微软 VS Code 都在用。它负责渲染字符、处理键盘输入、支持 ANSI 颜色和光标控制。
- 引入样式和脚本:
<link rel="stylesheet" href="xterm.css">和<script src="xterm.min.js"></script> - 创建容器 DOM 元素(比如
<div id="terminal"></div>),再初始化终端实例:const term = new Terminal({ cols: 80, rows: 24 }) - 调用
term.open(container)挂载到页面;用term.write("hello")可写入内容,term.onData(fn)监听用户按键 - 键盘输入需转换为 UTF-8 字符串,再通过 WebSocket 发送给后端,避免 Ctrl+C 等控制序列被截断
通信:WebSocket 建立持久双向连接
HTTP 请求一次一往返,不适合终端这种高频、低延迟、双向流式交互。WebSocket 在 TCP 上建立长连接,服务端可随时推送 stdout,客户端也能即时发 stdin。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 前端创建连接:
const ws = new WebSocket("ws://localhost:8080/pty"),注意协议前缀是ws://(开发)或wss://(生产) - 监听
ws.onopen后,调用term.focus()让终端获得焦点;ws.onmessage收到数据时,用term.write(data)渲染 - 每次
term.onData触发,就把字符传给ws.send();建议对 Backspace、Enter 等做简单预处理,保持语义清晰 - 连接异常时监听
onerror和onclose,给出友好提示并支持重连(可用ReconnectingWebSocket库)
后端:绑定终端进程并桥接数据流
后端本质是个“中间人”:fork 出真实 shell(如 bash 或 powershell),把 WebSocket 的收发数据,分别映射到该进程的 stdin/stdout。
- Node.js 场景常用
node-pty库创建伪终端(pty),它能正确处理行缓冲、信号(SIGINT)、尺寸变化(resize)等 - 每个 WebSocket 连接对应一个 pty 实例,用 Map 存储
socketId → pty关系,避免混用 - pty 的
onData事件触发时,将输出通过对应 WebSocket 发给前端;收到前端消息,则写入 pty.stdin - 注意进程生命周期管理:连接关闭时,应
pty.kill()并清理资源,防止僵尸进程堆积
安全与实用细节不能跳过
一个能跑起来的终端不难,但要稳定、安全、可用,得关注几个关键点:
- 限制执行权限:不要以 root 或管理员身份运行后端服务;生产环境建议用普通用户启动 pty,并禁用危险命令(如
rm -rf /)可通过白名单或沙箱机制约束 - 会话隔离:多个用户同时连接时,确保各自 pty 独立,互不干扰;可结合 JWT 或 session ID 标识用户上下文
- 终端尺寸同步:前端窗口缩放时,需捕获
window.onresize,计算新 cols/rows 并通过 WebSocket 发送 resize 指令给后端,再调用pty.resize() - 编码统一:全程使用 UTF-8,尤其 Windows 下 cmd/powershell 默认可能是 GBK,需在启动 pty 时指定
env: { ... , LANG: "en_US.UTF-8" }










