Files
SimpleRemoter/server/go/README.md

444 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SimpleRemoter Go TCP Server Framework
基于 Go 语言实现的高性能 TCP 服务端框架,用于替代原有的 C++ IOCP 服务端。
## 项目结构
```
server/go/
├── go.mod # Go 模块定义
├── auth/
│ └── auth.go # 授权验证模块 (TOKEN_AUTH + Heartbeat HMAC)
├── buffer/
│ └── buffer.go # 线程安全的动态缓冲区
├── connection/
│ ├── context.go # 连接上下文
│ ├── errors.go # 错误定义
│ └── manager.go # 连接管理器
├── protocol/
│ ├── parser.go # 协议解析器
│ ├── codec.go # 编解码和压缩 (ZSTD)
│ ├── header.go # 协议头解密 (8种加密方式)
│ └── commands.go # 命令常量和LOGIN_INFOR解析
├── server/
│ ├── server.go # TCP 服务器核心
│ └── pool.go # Goroutine 工作池
├── logger/
│ └── logger.go # 日志模块 (基于 zerolog)
├── hub/
│ └── hub.go # 在线设备注册表 + 事件订阅
├── wsauth/
│ └── wsauth.go # Web 鉴权 (challenge-response + 不透明 token)
├── web/
│ ├── embed.go # //go:embed 嵌入 HTML/xterm.js 等 web 资源
│ ├── server.go # HTTP server (静态页面 + REST + WS 路由)
│ ├── ws.go # WebSocket 连接生命周期
│ ├── ws_handlers.go # WS 消息分发与处理
│ └── assets/
│ ├── index.html # 从 ../../web/index.html sync 而来 (gitignored)
│ └── static/ # 第三方 xterm.js 资源 (checked in)
└── cmd/
└── main.go # 程序入口
```
## 核心特性
底层基础设施:
- **高并发**: 基于 Goroutine 池管理并发连接
- **协议兼容**: 支持原有客户端的多种协议标识 (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 | 值 | 用途 |
| ---- | ---- | ---- |
| `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 输出 |
### 服务端 → 客户端
| 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 日志,按需扩展。
## 快速开始
### 安装依赖
```bash
cd server/go
go mod tidy
```
### 编译
推荐用 Makefile编译前会自动从 `server/web/` 同步 HTML 到 `web/assets/`
```bash
make build # 当前平台
make windows # Windows amd64
make linux # Linux amd64
```
也可以直接用 `go build`,但要先手动 sync
```bash
make sync && go build -o simpleremoter-server ./cmd
```
VSCode F5 调试时由 `sync-web-assets` preLaunchTask 自动同步。
### 运行
```bash
./simpleremoter-server
```
默认监听 TCP 端口 `6543`被控设备HTTP 端口 `8080`(浏览器 Web UI。日志写到 `logs/server.log`
### 命令行参数
| 参数 | 默认值 | 说明 |
| ---- | ------ | ---- |
| `-port` / `-p` | `6543` | TCP 监听端口,分号分隔可多端口(如 `6543;6544` |
| `-http-port` | `8080` | HTTP 监听端口Web UI`0` 禁用 |
| `-no-console` | `false` | 关闭控制台输出(守护进程模式) |
### 环境变量
| 变量 | 说明 | 示例 |
| ---- | ---- | ---- |
| `YAMA_PWDHASH` | 密码的 SHA256 哈希值 (64位十六进制) | `61f04dd6...` |
| `YAMA_PWD` | 超级密码,用于 HMAC 签名验证;也作为 Web admin 密码的默认来源 | `your_super_password` |
| `YAMA_WEB_ADMIN_PASS` | Web UI 的 admin 密码(明文);优先于 `YAMA_PWD`。两者都未设置时 Web 登录禁用 | `your_admin_password` |
| `YAMA_SIGN_PASSWORD` | HMAC-SHA256 key used to sign CMD_MASTERSETTING replies; must match the client's expected value. Provision out-of-band. Unset → client refuses screen/file ops. | `<deployment-shared-secret>` |
| `YAMA_USERS_FILE` | Path to the JSON file that persists non-admin web users (allowed_groups, password hash, salt). Default is `users.json` in the working directory. | `users.json` |
```bash
# Linux/macOS
export YAMA_PWDHASH="61f04dd637a74ee34493fc1025de2c131022536da751c29e3ff4e9024d8eec43"
export YAMA_PWD="your_super_password"
./simpleremoter-server
# Windows PowerShell
$env:YAMA_PWDHASH="61f04dd637a74ee34493fc1025de2c131022536da751c29e3ff4e9024d8eec43"
$env:YAMA_PWD="your_super_password"
.\simpleremoter-server.exe
```
## 使用示例
完整的 TCP + Hub + Web 集成示例就是 [`cmd/main.go`](cmd/main.go),那是程序入口本身、也是最权威的范例 —— 包含 handler 装配、hub 注册、web HTTP/WS 服务、信号优雅关闭等。
如果只想用 TCP 框架做自定义服务端(不要 Web/Hub最小示例如下
```go
package main
import (
"os"
"os/signal"
"syscall"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/connection"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/logger"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/protocol"
"github.com/yuanyuanxiang/SimpleRemoter/server/go/server"
)
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
}
if data[0] == protocol.TokenLogin {
info, _ := protocol.ParseLoginInfo(data)
h.log.Info("login: %s (%s)", info.PCName, info.OsVerInfo)
}
}
func main() {
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("start: %v", err)
}
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
<-sig
srv.Stop()
}
```
## 配置选项
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| Port | 8080 | 监听端口 |
| MaxConnections | 10000 | 最大连接数 |
| MinWorkers | 4 | 最小工作协程数 |
| MaxWorkers | 100 | 最大工作协程数 |
| ReadBufferSize | 8192 | 读缓冲区大小 |
| WriteBufferSize | 8192 | 写缓冲区大小 |
| KeepAliveTime | 5min | 连接保活时间 |
| ReadTimeout | 2min | 读超时时间 |
| WriteTimeout | 30s | 写超时时间 |
## 日志配置
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| Level | Info | 日志级别 (Debug/Info/Warn/Error/Fatal) |
| Console | true | 是否输出到控制台 |
| File | "" | 日志文件路径 (空则不写文件) |
| MaxSize | 100 | 单个日志文件最大 MB |
| MaxBackups | 3 | 保留的旧日志文件数量 |
| MaxAge | 30 | 旧日志保留天数 |
| Compress | true | 是否压缩轮转的日志 |
日志示例输出:
```json
{"level":"info","module":"Server","time":"2025-12-19T13:17:32+01:00","message":"Server started on port 6543"}
{"level":"info","module":"Handler","event":"login","client_id":1,"ip":"192.168.0.92","computer":"DESKTOP-BI6RGEJ","os":"Windows 10","version":"Dec 19 2025","time":"2025-12-19T13:17:32+01:00"}
{"level":"debug","module":"Handler","time":"2025-12-19T13:17:47+01:00","message":"Heartbeat from client 1 (DESKTOP-BI6RGEJ)"}
```
## 协议格式
数据包格式与 C++ 版本兼容:
```
+----------+------------+------------+------------------+
| Flag | TotalLen | OrigLen | Compressed Data |
| (N bytes)| (4 bytes) | (4 bytes) | (variable) |
+----------+------------+------------+------------------+
```
### 协议标识
| 标识 | Flag长度 | 压缩方式 | 说明 |
|------|----------|----------|------|
| HELL | 8 bytes | ZSTD | 主要协议 |
| Hello? | 8 bytes | None | 无压缩协议 |
| Shine | 5 bytes | ZSTD | 备用协议 |
| <<FUCK>> | 11 bytes | ZSTD | 备用协议 |
### 协议头加密
支持8种加密方式服务端自动检测并解密
- V0 (Default): 动态密钥4种操作
- V1: 交替加减
- V2: 带旋转的异或
- V3: 带位置的动态密钥
- V4: 对称的伪随机异或
- V5: 带位移的动态密钥
- V6: 带位置的伪随机
- V7: 纯异或
### LOGIN_INFOR 结构
客户端登录信息结构体 (考虑 C++ 内存对齐)
| 字段 | 偏移 | 大小 | 说明 |
|------|------|------|------|
| bToken | 0 | 1 | 命令标识 (102) |
| OsVerInfoEx | 1 | 156 | 操作系统版本 |
| (padding) | 157 | 3 | 对齐填充 |
| dwCPUMHz | 160 | 4 | CPU 频率 |
| moduleVersion | 164 | 24 | 模块版本 |
| szPCName | 188 | 240 | 计算机名 |
| szMasterID | 428 | 20 | 主控 ID |
| bWebCamExist | 448 | 4 | 是否有摄像头 |
| dwSpeed | 452 | 4 | 网速 |
| szStartTime | 456 | 20 | 启动时间 |
| szReserved | 476 | 512 | 扩展字段(多字段以 `\|` 分隔) |
### Heartbeat 结构
客户端心跳包结构 (1024 字节)
| 字段 | 偏移 | 大小 | 说明 |
|------|------|------|------|
| Time | 0 | 8 | 时间戳 (uint64) |
| ActiveWnd | 8 | 512 | 当前活动窗口 |
| Ping | 520 | 4 | 延迟 (int) |
| HasSoftware | 524 | 4 | 软件标识 (int) |
| SN | 528 | 20 | 序列号 (用于授权验证) |
| Passcode | 548 | 44 | 授权码 (格式: v0-v1-v2-v3-v4-v5) |
| PwdHmac | 592 | 8 | HMAC 签名 (uint64) |
| Reserved | 600 | 424 | 保留字段 |
### HeartbeatACK 结构
服务端心跳响应结构 (32 字节)
| 字段 | 偏移 | 大小 | 说明 |
|------|------|------|------|
| Time | 0 | 8 | 原始时间戳 (uint64) |
| Authorized | 8 | 1 | 授权状态 (1=已授权, 0=未授权) |
| Reserved | 9 | 23 | 保留字段 |
### 授权验证流程
```
客户端 Heartbeat 服务端
│ │
│ SN + Passcode + PwdHmac │
│ ────────────────────────────────► │
│ │ 1. 验证 Passcode 格式
│ │ 2. 验证 Passcode 哈希
│ │ 3. 验证 HMAC 签名
│ HeartbeatACK │
│ ◄──────────────────────────────── │
│ (Authorized=1 或 0) │
```
## API 参考
### Server
```go
// 创建服务器
srv := server.New(config)
// 设置日志
srv.SetLogger(log)
// 设置事件处理器
srv.SetHandler(handler)
// 启动服务器
srv.Start()
// 停止服务器
srv.Stop()
// 发送数据到指定连接
srv.Send(ctx, data)
// 广播数据到所有连接
srv.Broadcast(data)
// 获取当前连接数
count := srv.ConnectionCount()
```
### Connection Context
```go
// 发送数据
ctx.Send(data)
// 关闭连接
ctx.Close()
// 获取客户端 IP
ip := ctx.GetPeerIP()
// 检查连接状态
closed := ctx.IsClosed()
// 获取/更新最后活跃时间 (线程安全)
lastActive := ctx.LastActive()
ctx.UpdateLastActive()
duration := ctx.TimeSinceLastActive()
// 设置/获取客户端信息
ctx.SetInfo(clientInfo)
info := ctx.GetInfo()
// 设置/获取用户数据
ctx.SetUserData(myData)
data := ctx.GetUserData()
```
### Protocol
```go
// 解析登录信息
info, err := protocol.ParseLoginInfo(data)
if err == nil {
fmt.Println(info.PCName) // 计算机名
fmt.Println(info.OsVerInfo) // 操作系统
fmt.Println(info.ModuleVersion) // 版本
fmt.Println(info.WebCamExist) // 是否有摄像头
}
// 获取扩展字段
reserved := info.ParseReserved() // 返回 []string
clientType := info.GetReservedField(0) // 客户端类型
cpuCores := info.GetReservedField(2) // CPU 核数
filePath := info.GetReservedField(4) // 文件路径
publicIP := info.GetReservedField(11) // 公网 IP
```
## 与 C++ 版本对比
| 特性 | C++ (IOCP) | Go |
| ---- | ---- | ---- |
| 并发模型 | IOCP + 线程池 | Goroutine 池 |
| 跨平台 | Windows | 全平台 |
| 内存管理 | 手动 | GC |
| 代码复杂度 | 高 | 低 |
| 压缩 / 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 互通 |
| 文件传输 / 摄像头 / 录音 等 | 已实现 | 暂未实现(按需扩展) |
## 依赖
- [github.com/klauspost/compress/zstd](https://github.com/klauspost/compress) - ZSTD 压缩
- [github.com/rs/zerolog](https://github.com/rs/zerolog) - 高性能日志
- [gopkg.in/natefinch/lumberjack.v2](https://github.com/natefinch/lumberjack) - 日志轮转
- [golang.org/x/text](https://pkg.go.dev/golang.org/x/text) - GBK 编码转换
## License
MIT License