60 lines
2.2 KiB
Markdown
60 lines
2.2 KiB
Markdown
# WebSSH 终端输入回显延迟修复
|
||
|
||
**日期**: 2026-05-26
|
||
**变更摘要**: 修复 WebSSH 终端输入字符不即时显示的问题 — 将 async-for (readline) 改为 read() + 降低 bufsize
|
||
|
||
## 动机
|
||
|
||
1. 用户在终端输入字符后,字符不会即时显示(echo 延迟),必须按 Enter 后才一次性出现
|
||
2. 根因:`async for data in shell.stdout` 内部调用 `readline()` → `readuntil('\n')`,会缓冲所有数据直到遇到换行符
|
||
3. 交互式终端中,单字符 echo 永远不包含换行符,导致所有输入字符被缓冲直到用户按 Enter
|
||
|
||
## 变更内容
|
||
|
||
### server/api/webssh.py — 2处修改
|
||
|
||
**修改1: `_read_shell_output()` 函数 — 消除 readline 缓冲**
|
||
- 旧:`async for data in shell.stdout:` (内部 readline,等换行才 yield)
|
||
- 新:`while not shell.stdout.at_eof(): data = await shell.stdout.read(4096)` (有数据即返回)
|
||
- `read(n)` 使用 `exact=False`,只要缓冲区有数据就立即返回(最多 n 字节),不等换行
|
||
|
||
**修改2: `create_process()` 调用 — 降低 bufsize**
|
||
- 旧:`conn.create_process(term_type="xterm-256color", term_size=(24, 80))` (默认 bufsize=128KB)
|
||
- 新:`conn.create_process(term_type="xterm-256color", term_size=(24, 80), bufsize=4096)`
|
||
- 两处 create_process(主连接 + 失效重试连接)均添加 `bufsize=4096`
|
||
- 4KB 缓冲区足够单次交互 echo,同时大幅降低延迟
|
||
|
||
## 技术分析
|
||
|
||
asyncssh 数据流路径:
|
||
```
|
||
async for data in shell.stdout
|
||
→ SSHStreamProcess.__aiter__()
|
||
→ SSHStreamSession.aiter()
|
||
→ readline()
|
||
→ readuntil('\n') ← 阻塞直到换行符
|
||
```
|
||
|
||
修复后路径:
|
||
```
|
||
while not shell.stdout.at_eof():
|
||
data = await shell.stdout.read(4096)
|
||
→ SSHStreamSession.read(4096, exact=False) ← 有数据即返回
|
||
```
|
||
|
||
## 涉及文件
|
||
|
||
- `server/api/webssh.py` — WebSSH WebSocket 处理(2处修改)
|
||
|
||
## 是否需迁移/重启
|
||
|
||
- 需重启后端(webssh.py 修改)
|
||
- 前端无改动
|
||
|
||
## 验证方式
|
||
|
||
1. 打开 terminal.html?server_id=8 → WebSocket 保持 OPEN,状态显示「已连接」
|
||
2. 输入 `whoami` → 字符即时显示(不等 Enter),按 Enter 后立即显示 `root`
|
||
3. 后端日志无 `shell creation failed` 错误
|
||
4. 终端交互流畅,无 echo 延迟
|