Feature(Go): Web terminal relay with PTY mode and graceful close (Phase 6)

This commit is contained in:
yuanyuanxiang
2026-05-18 15:03:42 +02:00
committed by yuanyuanxiang
parent 6485e800d6
commit d7f38ecfdb
7 changed files with 696 additions and 96 deletions

View File

@@ -43,31 +43,59 @@ server/go/
## 核心特性
底层基础设施:
- **高并发**: 基于 Goroutine 池管理并发连接
- **协议兼容**: 支持原有 C++ 客户端的多种协议标识 (Hell/Hello/Shine/Fuck)
- **协议头解密**: 支持8种协议头加密方式 (V0-V6 + Default)
- **授权验证**: 支持 TOKEN_AUTH 和 Heartbeat HMAC-SHA256 双重授权验证
- **XOR编码**: 支持 XOREncoder16 数据编码/解码
- **ZSTD 压缩**: 使用高效的 ZSTD 算法进行数据压缩
- **GBK编码**: 自动将 Windows 客户端的 GBK 编码转换为 UTF-8
- **线程安全**: Buffer、连接管理器和 LastActive 均为线程安全设计
- **优雅关闭**: 支持信号处理和优雅停机,自动释放资源
- **可配置**: 支持自定义端口、最大连接数、超时时间等
- **日志系统**: 基于 zerolog支持文件输出、日志轮转、客户端上下线记录
- **Web UI 服务**: 内建 HTTP server编译期 `//go:embed` 嵌入页面和静态资源,免外部文件依赖
- **协议兼容**: 支持原有客户端的多种协议标识 (Hell/Hello/Shine/Fuck)
- **协议头解密**: 支持 8 种协议头加密方式 (V0-V6 + Default)
- **授权验证**: TOKEN_AUTH 和 Heartbeat HMAC-SHA256 双重授权
- **XOR 编码 / ZSTD 压缩**: 与客户端完全兼容
- **字符编码自适应**: 根据客户端能力位选择 UTF-8 直通或 GBK→UTF-8 转换
- **线程安全 / 优雅关闭 / 多端口监听 / 结构化日志**
Web 应用能力 (Phase 3-7)
- **Web 鉴权**: challenge-response 登录 + 不透明 token与 users.json schema 互通
- **设备列表与监控**: 在线设备 / RTT / 活动窗口 / 分辨率 实时下发
- **Web 远程桌面**: 浏览器 WebCodecs 解码 H.264,二进制 WS 帧低延迟中继late-join 自动重发最近 IDR优雅 BYE 关闭防止客户端无意义重连
- **鼠标 / 键盘输入**: Win32 消息映射 (`WM_*` / `VK_*` / `MK_*`)MSG64 48 字节布局直传客户端
- **Web 终端**: xterm.js + Windows ConPTY / 旧 cmd 管道双模式;二进制 "TRM1" 帧分流;尺寸自适应;单设备单 viewer
- **用户与分组**: admin 可创建/删除 viewer 账号、配置 allowed_groupsusers.json 原子写入
## 支持的命令
当前已实现以下命令处理:
### 客户端 → 服务端
| 命令 | 值 | 说明 |
|------|-----|------|
| TOKEN_AUTH | 100 | 授权请求 (验证 SN + Passcode + HMAC) |
| TOKEN_HEARTBEAT | 101 | 心跳包 (支持 HMAC 授权验证,返回 Authorized 状态) |
| TOKEN_LOGIN | 102 | 客户端登录 |
| CMD_HEARTBEAT_ACK | 216 | 心跳响应 (包含 Authorized 字段) |
| Token | 值 | 用途 |
| ---- | ---- | ---- |
| `TOKEN_AUTH` | 100 | 授权请求SN + Passcode + HMAC |
| `TOKEN_HEARTBEAT` | 101 | 心跳包(携带 ActiveWnd / Ping / SN |
| `TOKEN_LOGIN` | 102 | 主连接登录 |
| `TOKEN_BITMAPINFO` | 115 | 屏幕子连接首包,含分辨率 + clientID |
| `TOKEN_FIRSTSCREEN` | 116 | 原始 BGRA 首帧Go 侧丢弃) |
| `TOKEN_NEXTSCREEN` | 117 | H.264 屏幕帧 |
| `TOKEN_SHELL_START` | 128 | 旧 cmd-pipe 终端子连接首包 |
| `TOKEN_KEYFRAME` | 134 | GOP 关键帧DEFAULT_GOP 无限大,实际未用) |
| `TOKEN_TERMINAL_START` | 232 | PTY 终端子连接首包 |
| `TOKEN_TERMINAL_CLOSE` | 233 | 终端关闭通知 |
| `TOKEN_CONN_AUTH` | 246 | 子连接身份握手,含 clientID |
| (raw bytes) | — | 终端 sub-conn 绑定后裸字节即 shell 输出 |
其他命令会被记录为 Debug 日志,可按需扩展。
### 服务端 → 客户端
| Command | 值 | 用途 |
| ---- | ---- | ---- |
| `COMMAND_SCREEN_SPY` | 16 | 启动屏幕捕获 |
| `COMMAND_SCREEN_CONTROL` | 20 | 鼠标 / 键盘输入MSG64 批次) |
| `COMMAND_NEXT` | 30 | 解除客户端读线程阻塞 |
| `COMMAND_SHELL` | 40 | 请求开启 shell 子连接 |
| `CMD_TERMINAL_RESIZE` | 81 | PTY 尺寸 (cols / rows int16 LE) |
| `COMMAND_BYE` | 204 | 优雅断开屏幕 / 终端 |
| `CMD_MASTERSETTING` | 215 | 主控配置 + HMAC 签名 (1000B) |
| `CMD_HEARTBEAT_ACK` | 216 | 心跳响应(携带 Authorized 字段) |
| `TOKEN_CONN_AUTH` | 246 | 子连接身份握手响应 (256B) |
未列出的命令字节会被记录为 Debug 日志,按需扩展。
## 快速开始
@@ -136,6 +164,10 @@ $env:YAMA_PWD="your_super_password"
## 使用示例
完整的 TCP + Hub + Web 集成示例就是 [`cmd/main.go`](cmd/main.go),那是程序入口本身、也是最权威的范例 —— 包含 handler 装配、hub 注册、web HTTP/WS 服务、信号优雅关闭等。
如果只想用 TCP 框架做自定义服务端(不要 Web/Hub最小示例如下
```go
package main
@@ -150,57 +182,32 @@ import (
"github.com/yuanyuanxiang/SimpleRemoter/server/go/server"
)
// 实现 Handler 接口
type MyHandler struct {
log *logger.Logger
}
func (h *MyHandler) OnConnect(ctx *connection.Context) {
h.log.ClientEvent("online", ctx.ID, ctx.GetPeerIP())
}
func (h *MyHandler) OnDisconnect(ctx *connection.Context) {
h.log.ClientEvent("offline", ctx.ID, ctx.GetPeerIP())
}
type MyHandler struct{ log *logger.Logger }
func (h *MyHandler) OnConnect(ctx *connection.Context) {}
func (h *MyHandler) OnDisconnect(ctx *connection.Context) {}
func (h *MyHandler) OnReceive(ctx *connection.Context, data []byte) {
if len(data) == 0 {
return
}
cmd := data[0]
switch cmd {
case protocol.TokenLogin:
if data[0] == protocol.TokenLogin {
info, _ := protocol.ParseLoginInfo(data)
h.log.Info("Client login: %s (%s)", info.PCName, info.OsVerInfo)
case protocol.TokenHeartbeat:
h.log.Debug("Heartbeat from client %d", ctx.ID)
h.log.Info("login: %s (%s)", info.PCName, info.OsVerInfo)
}
}
func main() {
// 配置日志 (控制台 + 文件)
logCfg := logger.DefaultConfig()
logCfg.File = "logs/server.log"
log := logger.New(logCfg)
// 配置服务器
config := server.DefaultConfig()
config.Port = 6543
// 创建并启动服务器
srv := server.New(config)
log := logger.New(logger.DefaultConfig())
srv := server.New(server.DefaultConfig())
srv.SetLogger(log.WithPrefix("Server"))
srv.SetHandler(&MyHandler{log: log})
if err := srv.Start(); err != nil {
log.Fatal("启动失败: %v", err)
log.Fatal("start: %v", err)
}
// 等待退出信号
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
<-sigChan
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
srv.Stop()
}
```
@@ -286,7 +293,7 @@ func main() {
| bWebCamExist | 448 | 4 | 是否有摄像头 |
| dwSpeed | 452 | 4 | 网速 |
| szStartTime | 456 | 20 | 启动时间 |
| szReserved | 476 | 512 | 扩展字段 (用`|`分隔) |
| szReserved | 476 | 512 | 扩展字段(多字段以 `\|` 分隔 |
### Heartbeat 结构
@@ -410,15 +417,19 @@ publicIP := info.GetReservedField(11) // 公网 IP
## 与 C++ 版本对比
| 特性 | C++ (IOCP) | Go |
|------|------------|-----|
| ---- | ---- | ---- |
| 并发模型 | IOCP + 线程池 | Goroutine 池 |
| 压缩算法 | ZSTD | ZSTD |
| 跨平台 | Windows | 全平台 |
| 内存管理 | 手动 | GC |
| 代码复杂度 | 高 | 低 |
| 协议头解密 | 8种方式 | 8种方式 |
| XOR编码 | XOREncoder16 | XOREncoder16 |
| 字符编码 | GBK | GBK -> UTF-8 |
| 压缩 / XOR / 头加密 | 完整 8 套加密方式 + XOREncoder16 + ZSTD | 完全对齐 |
| 字符编码 | GBK | UTF-8 直通 / GBK→UTF-8 (按客户端能力位) |
| 设备列表与监控 | MFC 列表控件 | Web UI |
| Web 远程桌面 | 内嵌浏览器 + H.264 | 完全对齐WebCodecs 解码) |
| 鼠标键盘转发 | 已实现 | 完全对齐 |
| Web 终端 | 内嵌 xterm.js + ConPTY | 完全对齐(含旧 cmd-pipe 兼容) |
| 用户 / 分组管理 | 已实现 | users.json schema 互通 |
| 文件传输 / 摄像头 / 录音 等 | 已实现 | 暂未实现(按需扩展) |
## 依赖