Initial commit
This commit is contained in:
784
remote/docs/开发/API文档.md
Normal file
784
remote/docs/开发/API文档.md
Normal file
@@ -0,0 +1,784 @@
|
||||
# API 文档
|
||||
|
||||
## 目录
|
||||
|
||||
- [认证接口](#认证接口)
|
||||
- [输入控制接口](#输入控制接口)
|
||||
- [流媒体接口](#流媒体接口)
|
||||
- [文件传输接口](#文件传输接口)
|
||||
- [WebSocket 消息类型](#websocket-消息类型)
|
||||
|
||||
---
|
||||
|
||||
## 认证接口
|
||||
|
||||
### POST /login
|
||||
|
||||
Web 登录接口,用于用户身份认证。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"password": "your_password"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| password | string | 否 | 用户密码(如果服务器未配置密码则不需要) |
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||||
"message": "Authentication disabled"
|
||||
}
|
||||
```
|
||||
|
||||
失败响应(400):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Password is required"
|
||||
}
|
||||
```
|
||||
|
||||
失败响应(401):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Invalid password"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/auth/login
|
||||
|
||||
API 登录接口,功能与 `/login` 相同。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"password": "your_password"
|
||||
}
|
||||
```
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/auth/verify
|
||||
|
||||
Token 验证接口,用于验证 JWT Token 是否有效。
|
||||
|
||||
**请求头**
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200):
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"valid": true,
|
||||
"userId": "default-user"
|
||||
}
|
||||
```
|
||||
|
||||
失败响应(401):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"valid": false,
|
||||
"error": "No token provided"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 输入控制接口
|
||||
|
||||
所有输入控制接口需要在请求头中携带有效的 Token:
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
### 鼠标操作
|
||||
|
||||
#### POST /api/input/mouse/move
|
||||
|
||||
移动鼠标到指定坐标。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"x": 100,
|
||||
"y": 200
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| x | number | 是 | 目标 X 坐标 |
|
||||
| y | number | 是 | 目标 Y 坐标 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200):
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
失败响应(400):
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Invalid coordinates"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/mouse/down
|
||||
|
||||
按下鼠标按键。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"button": "left"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| button | string | 否 | left | 鼠标按键:`left`、`right`、`middle` |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/mouse/up
|
||||
|
||||
释放鼠标按键。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"button": "left"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| button | string | 否 | left | 鼠标按键:`left`、`right`、`middle` |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/mouse/click
|
||||
|
||||
点击鼠标按键。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"button": "left"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| button | string | 否 | left | 鼠标按键:`left`、`right`、`middle` |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/mouse/wheel
|
||||
|
||||
鼠标滚轮滚动。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"delta": 100
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| delta | number | 是 | 滚动量(正数向上,负数向下) |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 键盘操作
|
||||
|
||||
#### POST /api/input/keyboard/down
|
||||
|
||||
按下键盘按键。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "a"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| key | string | 是 | 按键名称(如 `a`、`enter`、`ctrl`、`shift` 等) |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/keyboard/up
|
||||
|
||||
释放键盘按键。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "a"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| key | string | 是 | 按键名称 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/keyboard/press
|
||||
|
||||
按下并释放键盘按键(单击)。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "enter"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| key | string | 是 | 按键名称 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### POST /api/input/keyboard/type
|
||||
|
||||
输入文本字符串。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello World"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| text | string | 是 | 要输入的文本内容 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 流媒体接口
|
||||
|
||||
### GET /api/stream/info
|
||||
|
||||
获取流媒体信息。
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"stream": {
|
||||
"status": "running",
|
||||
"resolution": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"fps": 30,
|
||||
"bitrate": 2000,
|
||||
"gop": 60,
|
||||
"encoder": "libx264"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**字段说明**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| status | string | 流状态:`running` 或 `stopped` |
|
||||
| resolution | object | 屏幕分辨率 |
|
||||
| fps | number | 帧率 |
|
||||
| bitrate | number | 比特率(kbps) |
|
||||
| gop | number | GOP 大小 |
|
||||
| encoder | string | 编码器名称 |
|
||||
|
||||
---
|
||||
|
||||
### POST /api/stream/start
|
||||
|
||||
启动流媒体。
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Stream started"
|
||||
}
|
||||
```
|
||||
|
||||
已运行时响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Stream is already running"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/stream/stop
|
||||
|
||||
停止流媒体。
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Stream stopped"
|
||||
}
|
||||
```
|
||||
|
||||
未运行时响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Stream is not running"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 文件传输接口
|
||||
|
||||
### GET /api/files
|
||||
|
||||
获取文件列表。
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"files": [
|
||||
{
|
||||
"name": "example.txt",
|
||||
"size": 1024,
|
||||
"modifiedTime": "2024-01-01T12:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /api/files/browse
|
||||
|
||||
浏览目录内容。
|
||||
|
||||
**查询参数**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| path | string | 否 | 目录路径(默认为根目录) |
|
||||
|
||||
**请求示例**
|
||||
|
||||
```
|
||||
GET /api/files/browse?path=documents
|
||||
```
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"path": "documents",
|
||||
"directories": ["folder1", "folder2"],
|
||||
"files": [
|
||||
{
|
||||
"name": "file1.txt",
|
||||
"size": 1024,
|
||||
"modifiedTime": "2024-01-01T12:00:00.000Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/files/upload/start
|
||||
|
||||
开始上传会话(分块上传第一步)。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"filename": "example.zip",
|
||||
"totalChunks": 10,
|
||||
"fileSize": 52428800
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| filename | string | 是 | 文件名 |
|
||||
| totalChunks | number | 是 | 总分块数 |
|
||||
| fileSize | number | 是 | 文件总大小(字节) |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"fileId": "a1b2c3d4e5f6g7h8",
|
||||
"chunkSize": 5242880,
|
||||
"message": "Upload session started"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/files/upload/chunk
|
||||
|
||||
上传文件分块(分块上传第二步)。
|
||||
|
||||
**请求体**
|
||||
|
||||
Content-Type: `multipart/form-data`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| fileId | string | 上传会话 ID |
|
||||
| chunkIndex | number | 分块索引(从 0 开始) |
|
||||
| chunk | file | 分块数据 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"chunkIndex": 0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST /api/files/upload/merge
|
||||
|
||||
合并文件分块(分块上传第三步)。
|
||||
|
||||
**请求体**
|
||||
|
||||
```json
|
||||
{
|
||||
"fileId": "a1b2c3d4e5f6g7h8",
|
||||
"totalChunks": 10,
|
||||
"filename": "example.zip"
|
||||
}
|
||||
```
|
||||
|
||||
**参数说明**
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| fileId | string | 是 | 上传会话 ID |
|
||||
| totalChunks | number | 是 | 总分块数 |
|
||||
| filename | string | 是 | 最终文件名 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"filename": "example.zip"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET /api/files/:filename
|
||||
|
||||
下载文件。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| filename | string | 文件名 |
|
||||
|
||||
**请求头**
|
||||
|
||||
支持 Range 请求,可用于断点续传:
|
||||
|
||||
```
|
||||
Range: bytes=0-1023
|
||||
```
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200 或 206):
|
||||
- Content-Type: `application/octet-stream`
|
||||
- Accept-Ranges: `bytes`
|
||||
- Content-Length: 文件大小
|
||||
- Content-Range: 范围(仅 206 响应)
|
||||
|
||||
失败响应(404):
|
||||
```json
|
||||
{
|
||||
"error": "File not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### DELETE /api/files/:filename
|
||||
|
||||
删除文件。
|
||||
|
||||
**路径参数**
|
||||
|
||||
| 参数 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| filename | string | 文件名 |
|
||||
|
||||
**响应示例**
|
||||
|
||||
成功响应(200):
|
||||
```json
|
||||
{
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
失败响应(404):
|
||||
```json
|
||||
{
|
||||
"error": "File not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket 消息类型
|
||||
|
||||
### 客户端发送的消息类型
|
||||
|
||||
| 消息类型 | 说明 | 数据格式 |
|
||||
|----------|------|----------|
|
||||
| `mouseMove` | 鼠标移动 | `{ x: number, y: number }` |
|
||||
| `mouseDown` | 鼠标按下 | `{ button: 'left' \| 'right' \| 'middle' }` |
|
||||
| `mouseUp` | 鼠标释放 | `{ button: 'left' \| 'right' \| 'middle' }` |
|
||||
| `mouseWheel` | 鼠标滚轮 | `{ delta: number }` |
|
||||
| `keyDown` | 键盘按下 | `{ key: string }` |
|
||||
| `keyUp` | 键盘释放 | `{ key: string }` |
|
||||
| `clipboardGet` | 获取剪贴板内容 | 无额外数据 |
|
||||
| `clipboardSet` | 设置剪贴板内容 | `{ content: string }` |
|
||||
|
||||
### 服务端发送的消息类型
|
||||
|
||||
| 消息类型 | 说明 | 数据格式 |
|
||||
|----------|------|----------|
|
||||
| `screenInfo` | 屏幕信息 | `{ width: number, height: number }` |
|
||||
| `clipboardData` | 剪贴板数据 | `{ content: string }` |
|
||||
| `clipboardResult` | 剪贴板操作结果 | `{ success: boolean }` |
|
||||
| `clipboardTooLarge` | 剪贴板数据过大 | `{ message: string }` |
|
||||
|
||||
### 消息格式示例
|
||||
|
||||
**客户端发送鼠标移动:**
|
||||
```json
|
||||
{
|
||||
"type": "mouseMove",
|
||||
"data": {
|
||||
"x": 100,
|
||||
"y": 200
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**客户端发送键盘按键:**
|
||||
```json
|
||||
{
|
||||
"type": "keyDown",
|
||||
"data": {
|
||||
"key": "ctrl"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**服务端发送屏幕信息:**
|
||||
```json
|
||||
{
|
||||
"type": "screenInfo",
|
||||
"data": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**客户端请求剪贴板:**
|
||||
```json
|
||||
{
|
||||
"type": "clipboardGet"
|
||||
}
|
||||
```
|
||||
|
||||
**服务端返回剪贴板数据:**
|
||||
```json
|
||||
{
|
||||
"type": "clipboardData",
|
||||
"data": {
|
||||
"content": "复制的文本内容"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 错误响应格式
|
||||
|
||||
所有接口在发生错误时返回统一的错误格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "错误描述信息"
|
||||
}
|
||||
```
|
||||
|
||||
常见 HTTP 状态码:
|
||||
|
||||
| 状态码 | 说明 |
|
||||
|--------|------|
|
||||
| 200 | 请求成功 |
|
||||
| 400 | 请求参数错误 |
|
||||
| 401 | 未授权(Token 无效或过期) |
|
||||
| 404 | 资源不存在 |
|
||||
| 500 | 服务器内部错误 |
|
||||
382
remote/docs/开发/Electron迁移计划.md
Normal file
382
remote/docs/开发/Electron迁移计划.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# Electron 客户端迁移计划
|
||||
|
||||
## 项目概述
|
||||
|
||||
将现有的 `remote-screen-monitor` Web 应用迁移为 Electron 桌面客户端,解决浏览器安全限制导致的剪贴板和文件上传功能受限问题。
|
||||
|
||||
## 当前问题分析
|
||||
|
||||
### 浏览器安全限制
|
||||
|
||||
| 功能 | 当前状态 | 限制原因 |
|
||||
|------|---------|---------|
|
||||
| 剪贴板读取 | ❌ 受限 | `navigator.clipboard.readText()` 需要安全上下文 (HTTPS/localhost) |
|
||||
| 剪贴板写入 | ❌ 受限 | 同上 |
|
||||
| 文件选择 | ⚠️ 部分可用 | 只能通过 `<input type="file">` 选择,无法直接访问文件系统 |
|
||||
| 文件下载 | ⚠️ 部分可用 | 只能通过 Blob 下载,无法指定保存位置 |
|
||||
| 目录浏览 | ❌ 不可用 | File System Access API 需要安全上下文 |
|
||||
|
||||
### 受影响代码位置
|
||||
|
||||
- `public/index.html` - 剪贴板同步逻辑 (第 47-51, 93-97 行)
|
||||
- `public/js/file-panel.js` - 文件传输面板
|
||||
|
||||
---
|
||||
|
||||
## 迁移方案
|
||||
|
||||
### 架构设计
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Electron 主进程 (Main Process) │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ 剪贴板服务 │ │ 文件服务 │ │ 窗口管理 & 生命周期 │ │
|
||||
│ │ - readText │ │ - 浏览目录 │ │ - BrowserWindow │ │
|
||||
│ │ - writeText │ │ - 选择文件 │ │ - 托盘图标 │ │
|
||||
│ │ - readImage │ │ - 保存文件 │ │ - 系统菜单 │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────────────────┘ │
|
||||
│ │ │
|
||||
│ IPC Handler │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
IPC (进程间通信)
|
||||
│
|
||||
┌──────────────────────────▼──────────────────────────────────────┐
|
||||
│ Preload Script (预加载脚本) │
|
||||
│ - contextBridge.exposeInMainWorld │
|
||||
│ - 暴露安全的 API 给渲染进程 │
|
||||
│ - 验证和清理输入参数 │
|
||||
└──────────────────────────┬──────────────────────────────────────┘
|
||||
│
|
||||
window.electronAPI
|
||||
│
|
||||
┌──────────────────────────▼──────────────────────────────────────┐
|
||||
│ 渲染进程 (Renderer Process) │
|
||||
│ ┌─────────────────────────────────────────────────────────────┐│
|
||||
│ │ 现有 Web 应用 (public/) ││
|
||||
│ │ - index.html ││
|
||||
│ │ - js/app.js, player.js, input.js, file-panel.js ││
|
||||
│ │ - 通过 window.electronAPI 调用主进程功能 ││
|
||||
│ └─────────────────────────────────────────────────────────────┘│
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 详细任务清单
|
||||
|
||||
### 阶段一:项目初始化 (预计 30 分钟)
|
||||
|
||||
#### 1.1 安装 Electron 依赖
|
||||
|
||||
```bash
|
||||
npm install electron --save-dev
|
||||
npm install electron-builder --save-dev
|
||||
```
|
||||
|
||||
#### 1.2 创建 Electron 目录结构
|
||||
|
||||
```
|
||||
remote/
|
||||
├── electron/
|
||||
│ ├── main.js # 主进程入口
|
||||
│ ├── preload.js # 预加载脚本
|
||||
│ ├── ipc/
|
||||
│ │ ├── clipboard.js # 剪贴板 IPC 处理
|
||||
│ │ └── file.js # 文件系统 IPC 处理
|
||||
│ └── utils/
|
||||
│ └── paths.js # 路径工具函数
|
||||
├── public/ # 现有前端代码 (保持不变)
|
||||
├── src/ # 现有后端代码 (保持不变)
|
||||
└── package.json # 更新配置
|
||||
```
|
||||
|
||||
#### 1.3 更新 package.json
|
||||
|
||||
```json
|
||||
{
|
||||
"main": "electron/main.js",
|
||||
"scripts": {
|
||||
"electron:dev": "electron .",
|
||||
"electron:build": "electron-builder",
|
||||
"electron:build:win": "electron-builder --win --x64"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 阶段二:主进程开发 (预计 1 小时)
|
||||
|
||||
#### 2.1 创建主进程入口 (electron/main.js)
|
||||
|
||||
**功能需求:**
|
||||
- 创建 BrowserWindow
|
||||
- 加载远程服务器 URL 或本地 HTML
|
||||
- 配置 webPreferences (contextIsolation, nodeIntegration)
|
||||
- 注册 IPC Handler
|
||||
- 创建系统托盘图标
|
||||
- 处理窗口生命周期
|
||||
|
||||
**关键配置:**
|
||||
```javascript
|
||||
const win = new BrowserWindow({
|
||||
webPreferences: {
|
||||
nodeIntegration: false, // 安全:禁用 Node.js 集成
|
||||
contextIsolation: true, // 安全:启用上下文隔离
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
})
|
||||
|
||||
// 加载远程服务器
|
||||
win.loadURL('http://192.168.x.x:3000')
|
||||
// 或加载本地文件 (离线模式)
|
||||
// win.loadFile('public/index.html')
|
||||
```
|
||||
|
||||
#### 2.2 创建预加载脚本 (electron/preload.js)
|
||||
|
||||
**功能需求:**
|
||||
- 使用 contextBridge 暴露安全 API
|
||||
- 封装 IPC 调用
|
||||
- 提供类型安全的接口
|
||||
|
||||
**暴露的 API:**
|
||||
```javascript
|
||||
window.electronAPI = {
|
||||
// 剪贴板
|
||||
clipboard: {
|
||||
readText: () => Promise<string>,
|
||||
writeText: (text: string) => Promise<void>,
|
||||
readImage: () => Promise<string>, // base64
|
||||
writeImage: (base64: string) => Promise<void>
|
||||
},
|
||||
|
||||
// 文件系统
|
||||
file: {
|
||||
showOpenDialog: (options) => Promise<string[]>,
|
||||
showSaveDialog: (options) => Promise<string>,
|
||||
browseDirectory: (path: string) => Promise<FileInfo[]>,
|
||||
readFile: (path: string) => Promise<Buffer>,
|
||||
writeFile: (path: string, data: Buffer) => Promise<void>
|
||||
},
|
||||
|
||||
// 平台信息
|
||||
platform: {
|
||||
isElectron: true,
|
||||
os: 'win32' | 'darwin' | 'linux'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 实现剪贴板 IPC 处理 (electron/ipc/clipboard.js)
|
||||
|
||||
**功能需求:**
|
||||
- 使用 Electron 原生 `clipboard` 模块
|
||||
- 支持文本和图片格式
|
||||
- 替代现有的 PowerShell 方案
|
||||
|
||||
**优势:**
|
||||
- 无需启动 PowerShell 进程
|
||||
- 更快的响应速度
|
||||
- 更好的跨平台支持
|
||||
|
||||
#### 2.4 实现文件系统 IPC 处理 (electron/ipc/file.js)
|
||||
|
||||
**功能需求:**
|
||||
- 使用 Electron `dialog` 模块显示文件选择对话框
|
||||
- 支持本地目录浏览
|
||||
- 支持直接文件读写
|
||||
|
||||
---
|
||||
|
||||
### 阶段三:渲染进程适配 (预计 1 小时)
|
||||
|
||||
#### 3.1 创建适配层 (public/js/electron-adapter.js)
|
||||
|
||||
**功能需求:**
|
||||
- 检测运行环境 (Electron vs Browser)
|
||||
- 统一 API 接口
|
||||
- 自动选择最优实现
|
||||
|
||||
**设计模式:**
|
||||
```javascript
|
||||
const ClipboardAPI = {
|
||||
async readText() {
|
||||
if (window.electronAPI) {
|
||||
return window.electronAPI.clipboard.readText()
|
||||
}
|
||||
// 回退到浏览器 API
|
||||
return navigator.clipboard.readText()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2 修改剪贴板同步逻辑
|
||||
|
||||
**文件:** `public/index.html`
|
||||
|
||||
**修改内容:**
|
||||
- 替换 `navigator.clipboard` 调用为适配层 API
|
||||
- 添加 Electron 环境检测
|
||||
- 保留浏览器环境兼容性
|
||||
|
||||
#### 3.3 增强文件面板功能
|
||||
|
||||
**文件:** `public/js/file-panel.js`
|
||||
|
||||
**新增功能:**
|
||||
- 本地目录浏览 (Electron 环境)
|
||||
- 拖拽上传支持
|
||||
- 右键菜单 (删除、重命名)
|
||||
- 文件预览
|
||||
|
||||
---
|
||||
|
||||
### 阶段四:构建配置 (预计 30 分钟)
|
||||
|
||||
#### 4.1 配置 electron-builder
|
||||
|
||||
**package.json 配置:**
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"appId": "com.xuanchi.remote-screen",
|
||||
"productName": "XC Remote",
|
||||
"directories": {
|
||||
"output": "dist-electron"
|
||||
},
|
||||
"files": [
|
||||
"electron/**/*",
|
||||
"public/**/*"
|
||||
],
|
||||
"win": {
|
||||
"target": [
|
||||
{ "target": "nsis", "arch": ["x64"] },
|
||||
{ "target": "portable", "arch": ["x64"] }
|
||||
],
|
||||
"icon": "public/icon.ico"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 创建安装程序图标
|
||||
|
||||
- 需要 .ico 格式图标文件
|
||||
- 建议尺寸:256x256
|
||||
|
||||
---
|
||||
|
||||
### 阶段五:测试与优化 (预计 1 小时)
|
||||
|
||||
#### 5.1 功能测试清单
|
||||
|
||||
- [ ] 剪贴板文本同步 (Ctrl+C / Ctrl+V)
|
||||
- [ ] 剪贴板图片同步
|
||||
- [ ] 文件上传
|
||||
- [ ] 文件下载
|
||||
- [ ] 目录浏览
|
||||
- [ ] 远程屏幕显示
|
||||
- [ ] 鼠标键盘控制
|
||||
|
||||
#### 5.2 性能优化
|
||||
|
||||
- 剪贴板轮询优化 (避免频繁读取)
|
||||
- 文件传输进度优化
|
||||
- 内存使用监控
|
||||
|
||||
#### 5.3 安全检查
|
||||
|
||||
- [ ] contextIsolation 已启用
|
||||
- [ ] nodeIntegration 已禁用
|
||||
- [ ] preload 脚本无安全漏洞
|
||||
- [ ] IPC 消息验证
|
||||
|
||||
---
|
||||
|
||||
## 文件变更清单
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件路径 | 说明 |
|
||||
|---------|------|
|
||||
| `electron/main.js` | 主进程入口 |
|
||||
| `electron/preload.js` | 预加载脚本 |
|
||||
| `electron/ipc/clipboard.js` | 剪贴板 IPC 处理 |
|
||||
| `electron/ipc/file.js` | 文件系统 IPC 处理 |
|
||||
| `electron/utils/paths.js` | 路径工具 |
|
||||
| `public/js/electron-adapter.js` | 渲染进程适配层 |
|
||||
| `public/icon.ico` | 应用图标 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件路径 | 修改内容 |
|
||||
|---------|---------|
|
||||
| `package.json` | 添加 Electron 配置和脚本 |
|
||||
| `public/index.html` | 引入适配层,修改剪贴板逻辑 |
|
||||
| `public/js/file-panel.js` | 增强文件功能,添加 Electron 支持 |
|
||||
|
||||
---
|
||||
|
||||
## 时间估算
|
||||
|
||||
| 阶段 | 预计时间 |
|
||||
|------|---------|
|
||||
| 阶段一:项目初始化 | 30 分钟 |
|
||||
| 阶段二:主进程开发 | 1 小时 |
|
||||
| 阶段三:渲染进程适配 | 1 小时 |
|
||||
| 阶段四:构建配置 | 30 分钟 |
|
||||
| 阶段五:测试与优化 | 1 小时 |
|
||||
| **总计** | **4 小时** |
|
||||
|
||||
---
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
### 安全风险
|
||||
|
||||
1. **IPC 消息验证** - 所有 IPC 消息需要验证来源和参数
|
||||
2. **路径遍历攻击** - 文件操作需要验证路径合法性
|
||||
3. **远程代码加载** - 如果加载远程 URL,需要确保服务器安全
|
||||
|
||||
### 兼容性考虑
|
||||
|
||||
1. **保留浏览器兼容** - 应用应同时支持浏览器和 Electron 环境
|
||||
2. **优雅降级** - 在浏览器环境中使用现有方案
|
||||
|
||||
### 性能考虑
|
||||
|
||||
1. **剪贴板监听** - 避免频繁轮询剪贴板
|
||||
2. **大文件传输** - 使用流式处理避免内存溢出
|
||||
|
||||
---
|
||||
|
||||
## 后续增强
|
||||
|
||||
1. **自动更新** - 集成 electron-updater
|
||||
2. **离线模式** - 支持本地 HTML 加载
|
||||
3. **多窗口** - 支持多显示器场景
|
||||
4. **快捷键** - 全局快捷键支持
|
||||
5. **系统托盘** - 最小化到托盘
|
||||
|
||||
---
|
||||
|
||||
## 结论
|
||||
|
||||
通过 Electron 迁移,可以完全解决浏览器安全限制问题:
|
||||
|
||||
| 功能 | 迁移前 | 迁移后 |
|
||||
|------|--------|--------|
|
||||
| 剪贴板读取 | ❌ 受限 | ✅ 完全可用 |
|
||||
| 剪贴板写入 | ❌ 受限 | ✅ 完全可用 |
|
||||
| 文件选择 | ⚠️ 受限 | ✅ 完全可用 |
|
||||
| 目录浏览 | ❌ 不可用 | ✅ 完全可用 |
|
||||
| 文件保存 | ⚠️ 受限 | ✅ 完全可用 |
|
||||
|
||||
迁移后,用户体验将显著提升,同时保持与现有服务端的完全兼容。
|
||||
1108
remote/docs/开发/前端开发.md
Normal file
1108
remote/docs/开发/前端开发.md
Normal file
File diff suppressed because it is too large
Load Diff
627
remote/docs/开发/安全指南.md
Normal file
627
remote/docs/开发/安全指南.md
Normal file
@@ -0,0 +1,627 @@
|
||||
# 安全指南
|
||||
|
||||
本文档详细说明了 Remote Control 项目的安全机制和最佳实践。
|
||||
|
||||
## 目录
|
||||
|
||||
- [1. 密码安全](#1-密码安全)
|
||||
- [2. JWT Token 管理](#2-jwt-token-管理)
|
||||
- [3. 认证中间件](#3-认证中间件)
|
||||
- [4. 安全建议](#4-安全建议)
|
||||
- [5. 常见安全风险](#5-常见安全风险)
|
||||
|
||||
---
|
||||
|
||||
## 1. 密码安全
|
||||
|
||||
### 1.1 bcrypt 哈希算法
|
||||
|
||||
本项目使用 bcrypt 算法对密码进行哈希处理。bcrypt 是一种专门为密码存储设计的哈希算法,具有以下优势:
|
||||
|
||||
- **自带盐值**:每次哈希自动生成随机盐值,防止彩虹表攻击
|
||||
- **计算成本可调**:通过 cost factor 控制计算复杂度,抵御暴力破解
|
||||
- **抗 GPU/ASIC 攻击**:内存密集型设计,不适合硬件加速
|
||||
|
||||
```javascript
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
const BCRYPT_COST = 12;
|
||||
|
||||
async function hashPassword(password) {
|
||||
return bcrypt.hash(password, BCRYPT_COST);
|
||||
}
|
||||
|
||||
async function verifyPassword(password, hash) {
|
||||
return bcrypt.compare(password, hash);
|
||||
}
|
||||
```
|
||||
|
||||
**Cost Factor 说明**:
|
||||
- 项目使用 `BCRYPT_COST = 12`(约 4096 轮迭代)
|
||||
- Cost 值每增加 1,计算时间翻倍
|
||||
- 推荐范围:10-12(生产环境),可根据服务器性能调整
|
||||
|
||||
### 1.2 密码迁移脚本使用
|
||||
|
||||
项目提供了密码迁移脚本 `scripts/migrate-password.js`,用于将明文密码转换为 bcrypt 哈希值。
|
||||
|
||||
**使用方法**:
|
||||
|
||||
```bash
|
||||
node scripts/migrate-password.js
|
||||
```
|
||||
|
||||
**交互示例**:
|
||||
|
||||
```
|
||||
=== 密码迁移脚本 ===
|
||||
此脚本将明文密码转换为 bcrypt 哈希值
|
||||
|
||||
请输入要哈希的密码 (或输入 q 退出): MySecurePassword123
|
||||
|
||||
--- 结果 ---
|
||||
明文密码: MySecurePassword123
|
||||
bcrypt 哈希: $2b$10$abcdefghijklmnopqrstuvwxABCDEFGHIJ
|
||||
|
||||
请将哈希值更新到环境变量或配置文件中
|
||||
```
|
||||
|
||||
**配置方式**:
|
||||
|
||||
将生成的哈希值配置到环境变量:
|
||||
|
||||
```bash
|
||||
# .env 文件
|
||||
REMOTE_SECURITY_PASSWORD=$2b$10$abcdefghijklmnopqrstuvwxABCDEFGHIJ
|
||||
```
|
||||
|
||||
### 1.3 明文密码 vs 哈希密码
|
||||
|
||||
系统支持两种密码配置方式,但**强烈推荐使用哈希密码**:
|
||||
|
||||
| 特性 | 明文密码 | 哈希密码 |
|
||||
|------|----------|----------|
|
||||
| 配置方式 | `REMOTE_SECURITY_PASSWORD=mypassword` | `REMOTE_SECURITY_PASSWORD=$2b$12$...` |
|
||||
| 识别方式 | 不以 `$2b$` 开头 | 以 `$2b$` 开头 |
|
||||
| 安全性 | 低 | 高 |
|
||||
| 泄露风险 | 配置文件泄露即密码泄露 | 哈希值泄露无法反推原密码 |
|
||||
|
||||
**密码验证流程**:
|
||||
|
||||
```javascript
|
||||
async authenticate(password) {
|
||||
if (this.isHashed) {
|
||||
return await this.verifyPassword(password, this.passwordHash);
|
||||
} else {
|
||||
return password === this.passwordHash;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> ⚠️ **警告**:明文密码仅用于开发测试环境,生产环境必须使用 bcrypt 哈希密码。
|
||||
|
||||
---
|
||||
|
||||
## 2. JWT Token 管理
|
||||
|
||||
### 2.1 Token 生成
|
||||
|
||||
Token 使用 `jsonwebtoken` 库生成,包含用户标识和签发时间:
|
||||
|
||||
```javascript
|
||||
const TOKEN_EXPIRY = '24h';
|
||||
|
||||
generateToken(payload) {
|
||||
const tokenPayload = {
|
||||
userId: payload.userId || 'default-user',
|
||||
iat: Math.floor(Date.now() / 1000)
|
||||
};
|
||||
|
||||
const options = {
|
||||
expiresIn: TOKEN_EXPIRY
|
||||
};
|
||||
|
||||
return jwt.sign(tokenPayload, this.secret, options);
|
||||
}
|
||||
```
|
||||
|
||||
**Token 结构**:
|
||||
- Header:算法类型 (HS256)
|
||||
- Payload:`userId`、`iat`(签发时间)、`exp`(过期时间)
|
||||
- Signature:使用密钥签名
|
||||
|
||||
### 2.2 Token 验证
|
||||
|
||||
```javascript
|
||||
verifyToken(token) {
|
||||
if (!token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const decoded = jwt.verify(token, this.secret);
|
||||
return decoded;
|
||||
} catch (error) {
|
||||
if (error.name === 'TokenExpiredError') {
|
||||
console.warn('Token expired', { expiredAt: error.expiredAt });
|
||||
} else if (error.name === 'JsonWebTokenError') {
|
||||
console.warn('Invalid token', { error: error.message });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**验证结果**:
|
||||
- 成功:返回解码后的 payload 对象
|
||||
- Token 过期:返回 `null`,记录过期时间
|
||||
- Token 无效:返回 `null`,记录错误信息
|
||||
|
||||
### 2.3 Token 有效期
|
||||
|
||||
- **默认有效期**:24 小时
|
||||
- **续期策略**:Token 过期后需重新认证
|
||||
- **建议**:根据业务需求调整有效期
|
||||
|
||||
```javascript
|
||||
const TOKEN_EXPIRY = '24h';
|
||||
|
||||
const options = {
|
||||
expiresIn: TOKEN_EXPIRY
|
||||
};
|
||||
```
|
||||
|
||||
### 2.4 密钥配置
|
||||
|
||||
密钥优先级顺序:
|
||||
|
||||
```javascript
|
||||
_getSecret() {
|
||||
if (process.env.JWT_SECRET) {
|
||||
return process.env.JWT_SECRET;
|
||||
}
|
||||
|
||||
if (process.env.REMOTE_SECURITY_PASSWORD) {
|
||||
return process.env.REMOTE_SECURITY_PASSWORD;
|
||||
}
|
||||
|
||||
return 'remote-control-default-secret-change-in-production';
|
||||
}
|
||||
```
|
||||
|
||||
**配置优先级**:
|
||||
1. `JWT_SECRET` - 推荐使用
|
||||
2. `REMOTE_SECURITY_PASSWORD` - 备用选项
|
||||
3. 默认密钥 - 仅开发环境,生产环境必须配置
|
||||
|
||||
**推荐配置**:
|
||||
|
||||
```bash
|
||||
# .env 文件
|
||||
JWT_SECRET=your-very-long-random-secret-key-at-least-32-characters
|
||||
REMOTE_SECURITY_PASSWORD=$2b$12$your-bcrypt-hashed-password
|
||||
```
|
||||
|
||||
> ⚠️ **警告**:生产环境必须设置 `JWT_SECRET`,使用默认密钥将导致严重安全风险。
|
||||
|
||||
---
|
||||
|
||||
## 3. 认证中间件
|
||||
|
||||
### 3.1 Token 提取方式
|
||||
|
||||
认证中间件支持多种 Token 提取方式:
|
||||
|
||||
```javascript
|
||||
function extractToken(req) {
|
||||
if (req.headers.authorization) {
|
||||
const parts = req.headers.authorization.split(' ');
|
||||
if (parts.length === 2 && parts[0] === 'Bearer') {
|
||||
return parts[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (req.cookies) {
|
||||
if (req.cookies.token) {
|
||||
return req.cookies.token;
|
||||
}
|
||||
if (req.cookies.auth) {
|
||||
return req.cookies.auth;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.query && req.query.token) {
|
||||
return req.query.token;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
**提取顺序**:
|
||||
1. **Authorization Header**:`Authorization: Bearer <token>`
|
||||
2. **Cookie**:`token` 或 `auth` 字段
|
||||
3. **Query Parameter**:`?token=<token>`
|
||||
|
||||
### 3.2 密码验证
|
||||
|
||||
当 Token 无效或不存在时,支持通过密码进行认证:
|
||||
|
||||
```javascript
|
||||
const password = req.query.password || req.body?.password;
|
||||
|
||||
if (password) {
|
||||
const isValid = await authService.authenticate(password);
|
||||
if (isValid) {
|
||||
req.user = { userId: 'default-user' };
|
||||
res.locals.authenticated = true;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**密码传递方式**:
|
||||
- Query Parameter:`?password=<password>`
|
||||
- Request Body:`{ "password": "<password>" }`
|
||||
|
||||
### 3.3 认证流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 认证中间件流程 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. 检查是否配置密码 │
|
||||
│ └─ 无密码配置 → 跳过认证,允许访问 │
|
||||
│ │
|
||||
│ 2. 提取 Token │
|
||||
│ ├─ Authorization Header │
|
||||
│ ├─ Cookie │
|
||||
│ └─ Query Parameter │
|
||||
│ │
|
||||
│ 3. 验证 Token │
|
||||
│ ├─ 有效 → 设置用户信息,允许访问 │
|
||||
│ └─ 无效/过期 → 继续下一步 │
|
||||
│ │
|
||||
│ 4. 提取密码 │
|
||||
│ ├─ Query Parameter │
|
||||
│ └─ Request Body │
|
||||
│ │
|
||||
│ 5. 验证密码 │
|
||||
│ ├─ 正确 → 允许访问 │
|
||||
│ └─ 错误 → 返回 401 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**完整代码**:
|
||||
|
||||
```javascript
|
||||
async function authMiddleware(req, res, next) {
|
||||
const authService = AuthService.getInstance();
|
||||
const tokenManager = TokenManager.getInstance();
|
||||
|
||||
if (!authService.hasPassword()) {
|
||||
req.user = { userId: 'default-user' };
|
||||
res.locals.authenticated = true;
|
||||
return next();
|
||||
}
|
||||
|
||||
const token = extractToken(req);
|
||||
|
||||
if (token) {
|
||||
const decoded = tokenManager.verifyToken(token);
|
||||
if (decoded) {
|
||||
req.user = { userId: decoded.userId };
|
||||
res.locals.authenticated = true;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
const password = req.query.password || req.body?.password;
|
||||
|
||||
if (password) {
|
||||
const isValid = await authService.authenticate(password);
|
||||
if (isValid) {
|
||||
req.user = { userId: 'default-user' };
|
||||
res.locals.authenticated = true;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
res.status(401).json({
|
||||
error: 'Authentication required',
|
||||
code: 'AUTH_REQUIRED'
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 安全建议
|
||||
|
||||
### 4.1 使用强密码
|
||||
|
||||
- **长度**:至少 12 个字符
|
||||
- **复杂度**:包含大小写字母、数字、特殊字符
|
||||
- **避免**:常见单词、生日、连续字符
|
||||
|
||||
```bash
|
||||
# 生成强密码示例
|
||||
openssl rand -base64 16
|
||||
```
|
||||
|
||||
### 4.2 启用 HTTPS
|
||||
|
||||
**为什么需要 HTTPS**:
|
||||
- 加密传输数据,防止中间人攻击
|
||||
- 保护 Token 和密码不被窃听
|
||||
- 防止内容被篡改
|
||||
|
||||
**配置示例(Nginx)**:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name your-domain.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
ssl_ciphers HIGH:!aNULL:!MD5;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 定期更换密钥
|
||||
|
||||
**建议更换周期**:
|
||||
- JWT_SECRET:每 3-6 个月
|
||||
- 密码:每 3 个月
|
||||
|
||||
**更换步骤**:
|
||||
|
||||
1. 生成新密钥:
|
||||
```bash
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
2. 更新环境变量
|
||||
3. 重启服务(会使所有现有 Token 失效)
|
||||
4. 通知用户重新登录
|
||||
|
||||
### 4.4 限制访问 IP
|
||||
|
||||
**Nginx 配置示例**:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
allow 192.168.1.0/24;
|
||||
allow 10.0.0.0/8;
|
||||
deny all;
|
||||
|
||||
proxy_pass http://localhost:3000;
|
||||
}
|
||||
```
|
||||
|
||||
**应用层限制**:
|
||||
|
||||
```javascript
|
||||
const ALLOWED_IPS = ['192.168.1.0/24', '10.0.0.0/8'];
|
||||
|
||||
function ipFilter(req, res, next) {
|
||||
const clientIp = req.ip || req.connection.remoteAddress;
|
||||
|
||||
if (!isIpAllowed(clientIp, ALLOWED_IPS)) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5 使用 FRP Token
|
||||
|
||||
如果通过 FRP 进行内网穿透,建议配置 FRP Token:
|
||||
|
||||
**frps.ini(服务端)**:
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_port = 7000
|
||||
authentication_method = token
|
||||
token = your-secure-frp-token
|
||||
```
|
||||
|
||||
**frpc.ini(客户端)**:
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr = your-server.com
|
||||
server_port = 7000
|
||||
authentication_method = token
|
||||
token = your-secure-frp-token
|
||||
|
||||
[web]
|
||||
type = http
|
||||
local_port = 3000
|
||||
custom_domains = your-domain.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见安全风险
|
||||
|
||||
### 5.1 密码泄露
|
||||
|
||||
**风险来源**:
|
||||
- 配置文件被提交到版本控制
|
||||
- 日志中记录敏感信息
|
||||
- 服务器被入侵
|
||||
|
||||
**防护措施**:
|
||||
|
||||
1. **使用环境变量**:
|
||||
```bash
|
||||
# .env 文件(添加到 .gitignore)
|
||||
REMOTE_SECURITY_PASSWORD=$2b$12$...
|
||||
JWT_SECRET=your-secret-key
|
||||
```
|
||||
|
||||
2. **检查日志输出**:
|
||||
```javascript
|
||||
// 错误示例
|
||||
logger.info('User login', { password: password });
|
||||
|
||||
// 正确示例
|
||||
logger.info('User login', { userId: userId });
|
||||
```
|
||||
|
||||
3. **文件权限控制**:
|
||||
```bash
|
||||
chmod 600 .env
|
||||
chown app:app .env
|
||||
```
|
||||
|
||||
### 5.2 中间人攻击
|
||||
|
||||
**攻击场景**:
|
||||
- HTTP 明文传输被监听
|
||||
- 公共 WiFi 环境被劫持
|
||||
- DNS 污染
|
||||
|
||||
**防护措施**:
|
||||
|
||||
1. **强制 HTTPS**:
|
||||
```javascript
|
||||
app.use((req, res, next) => {
|
||||
if (!req.secure && req.get('x-forwarded-proto') !== 'https') {
|
||||
return res.redirect(`https://${req.get('host')}${req.url}`);
|
||||
}
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
2. **设置安全响应头**:
|
||||
```javascript
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
3. **使用 VPN**:在公共网络环境访问敏感服务
|
||||
|
||||
### 5.3 XSS 攻击
|
||||
|
||||
**攻击场景**:
|
||||
- 用户输入未转义直接渲染
|
||||
- 恶意脚本窃取 Token
|
||||
- Cookie 被劫持
|
||||
|
||||
**防护措施**:
|
||||
|
||||
1. **设置 Cookie 安全属性**:
|
||||
```javascript
|
||||
res.cookie('token', token, {
|
||||
httpOnly: true,
|
||||
secure: true,
|
||||
sameSite: 'strict',
|
||||
maxAge: 24 * 60 * 60 * 1000
|
||||
});
|
||||
```
|
||||
|
||||
2. **内容安全策略(CSP)**:
|
||||
```javascript
|
||||
res.setHeader(
|
||||
'Content-Security-Policy',
|
||||
"default-src 'self'; script-src 'self'"
|
||||
);
|
||||
```
|
||||
|
||||
3. **输入验证和转义**:
|
||||
```javascript
|
||||
const escapeHtml = (str) => {
|
||||
return str
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
};
|
||||
```
|
||||
|
||||
### 5.4 CSRF 攻击
|
||||
|
||||
**攻击场景**:
|
||||
- 恶意网站发送伪造请求
|
||||
- 利用用户已认证状态
|
||||
- 执行未授权操作
|
||||
|
||||
**防护措施**:
|
||||
|
||||
1. **SameSite Cookie**:
|
||||
```javascript
|
||||
res.cookie('token', token, {
|
||||
sameSite: 'strict'
|
||||
});
|
||||
```
|
||||
|
||||
2. **CSRF Token**:
|
||||
```javascript
|
||||
const csrf = require('csurf');
|
||||
const csrfProtection = csrf({ cookie: true });
|
||||
|
||||
app.get('/form', csrfProtection, (req, res) => {
|
||||
res.json({ csrfToken: req.csrfToken() });
|
||||
});
|
||||
|
||||
app.post('/submit', csrfProtection, (req, res) => {
|
||||
// 处理请求
|
||||
});
|
||||
```
|
||||
|
||||
3. **验证 Referer 头**:
|
||||
```javascript
|
||||
app.use((req, res, next) => {
|
||||
const referer = req.get('referer');
|
||||
const allowedOrigins = ['https://your-domain.com'];
|
||||
|
||||
if (referer && !allowedOrigins.some(origin => referer.startsWith(origin))) {
|
||||
return res.status(403).json({ error: 'Invalid referer' });
|
||||
}
|
||||
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 安全检查清单
|
||||
|
||||
在部署到生产环境前,请确保完成以下检查:
|
||||
|
||||
- [ ] 使用 bcrypt 哈希密码(非明文)
|
||||
- [ ] 配置强随机 JWT_SECRET
|
||||
- [ ] 启用 HTTPS
|
||||
- [ ] 设置 Cookie 安全属性(httpOnly, secure, sameSite)
|
||||
- [ ] 配置安全响应头(HSTS, CSP)
|
||||
- [ ] 敏感文件未提交到版本控制
|
||||
- [ ] 日志不包含敏感信息
|
||||
- [ ] 配置 IP 访问限制(如适用)
|
||||
- [ ] FRP 配置 Token 认证(如适用)
|
||||
- [ ] 定期更换密钥和密码
|
||||
|
||||
---
|
||||
|
||||
## 相关文件
|
||||
|
||||
- [AuthService.js](../../src/services/auth/AuthService.js) - 认证服务实现
|
||||
- [TokenManager.js](../../src/services/auth/TokenManager.js) - Token 管理器
|
||||
- [auth.js](../../src/middlewares/auth.js) - 认证中间件
|
||||
- [migrate-password.js](../../scripts/migrate-password.js) - 密码迁移脚本
|
||||
1261
remote/docs/开发/服务开发.md
Normal file
1261
remote/docs/开发/服务开发.md
Normal file
File diff suppressed because it is too large
Load Diff
646
remote/docs/开发/架构设计.md
Normal file
646
remote/docs/开发/架构设计.md
Normal file
@@ -0,0 +1,646 @@
|
||||
# 架构设计文档
|
||||
|
||||
## 1. 整体架构说明
|
||||
|
||||
本项目是一个远程桌面控制服务端应用,采用模块化、事件驱动的架构设计。核心设计理念包括:
|
||||
|
||||
- **依赖注入 (DI)**: 通过容器管理服务生命周期和依赖关系
|
||||
- **事件驱动**: 使用事件总线实现模块间松耦合通信
|
||||
- **分层架构**: 清晰的职责划分,便于维护和扩展
|
||||
|
||||
### 1.1 架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 客户端 (Browser) │
|
||||
│ ┌─────────────────┬─────────────────┐ │
|
||||
│ │ HTTP 请求 │ WebSocket 连接 │ │
|
||||
└────────────────────┼─────────────────┼─────────────────┼────────────────────┘
|
||||
│ │
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ App.js │
|
||||
│ (应用主类 - 启动入口) │
|
||||
└─────────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌────────────┼────────────┐
|
||||
▼ ▼ ▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||||
│ Container │ │ EventBus │ │ ErrorHandler │
|
||||
│ (依赖注入) │ │ (事件总线) │ │ (错误处理) │
|
||||
└──────────────┘ └──────────────┘ └──────────────┘
|
||||
│
|
||||
│ 服务注册与解析
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 服务层 (Services) │
|
||||
├─────────────┬─────────────┬─────────────┬─────────────┬─────────────────────┤
|
||||
│ Auth │ Stream │ Input │ Clipboard │ Network │
|
||||
│ 认证服务 │ 流媒体 │ 输入控制 │ 剪贴板 │ FRP/Gitea │
|
||||
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 服务器层 (Server) │
|
||||
├─────────────────────────────┬───────────────────────────────────────────────┤
|
||||
│ HTTP Server │ WebSocket Server │
|
||||
│ (静态资源/API路由) │ (实时通信/流传输) │
|
||||
├─────────────────────────────┼───────────────────────────────────────────────┤
|
||||
│ InputHandler │ StreamBroadcaster │
|
||||
│ (输入处理) │ (流广播) │
|
||||
└─────────────────────────────┴───────────────────────────────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────┐
|
||||
│ 系统层 (System) │
|
||||
├─────────────────────────────┬───────────────────────────────────────────────┤
|
||||
│ FFmpeg Encoder │ PowerShell Input │
|
||||
│ (视频编码) │ (系统输入模拟) │
|
||||
└─────────────────────────────┴───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.2 启动流程时序图
|
||||
|
||||
```
|
||||
┌──────┐ ┌─────────┐ ┌───────────┐ ┌──────────┐ ┌─────────┐
|
||||
│ App │ │Container│ │ErrorHandler│ │HTTPServer│ │WSServer │
|
||||
└──┬───┘ └────┬────┘ └─────┬─────┘ └────┬─────┘ └────┬────┘
|
||||
│ │ │ │ │
|
||||
│ bootstrap() │ │ │ │
|
||||
│─────────────>│ │ │ │
|
||||
│ │ │ │ │
|
||||
│ register services │ │ │
|
||||
│─────────────────────────────>│ │ │
|
||||
│ │ │ │ │
|
||||
│ start() │ │ │ │
|
||||
│──────────────┼───────────────┼───────────────┼───────────────┤
|
||||
│ │ │ │ │
|
||||
│ │ initialize() │ │ │
|
||||
│ │──────────────>│ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ │ start() │
|
||||
│ │ │ │──────────────>│
|
||||
│ │ │ │ │
|
||||
│ │ │ │ start() │
|
||||
│ │ │──────────────>│ │
|
||||
│ │ │ │ │
|
||||
│ emit(APP_START) │ │ │
|
||||
│─────────────────────────────────────────────────────────────────
|
||||
│ │ │ │ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心模块说明
|
||||
|
||||
### 2.1 App.js - 应用主类
|
||||
|
||||
应用主类负责整个应用的生命周期管理,包括启动、运行和关闭。
|
||||
|
||||
#### 职责
|
||||
|
||||
- **引导启动 (Bootstrap)**: 初始化容器和事件总线,注册所有服务
|
||||
- **启动服务 (Start)**: 按顺序启动所有已注册的服务
|
||||
- **优雅关闭 (Graceful Shutdown)**: 处理系统信号,安全停止所有服务
|
||||
|
||||
#### 启动流程
|
||||
|
||||
```javascript
|
||||
const App = require('./core/App');
|
||||
|
||||
const app = new App();
|
||||
|
||||
await app.bootstrap();
|
||||
await app.start();
|
||||
```
|
||||
|
||||
#### 服务注册
|
||||
|
||||
App.js 通过 Container 注册以下服务:
|
||||
|
||||
| 服务名称 | 类/模块 | 职责 |
|
||||
|---------|--------|------|
|
||||
| `config` | config | 配置管理 |
|
||||
| `logger` | logger | 日志记录 |
|
||||
| `errorHandler` | ErrorHandler | 全局错误处理 |
|
||||
| `authService` | AuthService | 用户认证 |
|
||||
| `tokenManager` | TokenManager | JWT Token 管理 |
|
||||
| `ffmpegEncoder` | FFmpegEncoder | 视频流编码 |
|
||||
| `inputService` | PowerShellInput | 输入控制 |
|
||||
| `inputHandler` | InputHandler | 输入消息处理 |
|
||||
| `clipboardService` | ClipboardService | 剪贴板操作 |
|
||||
| `fileService` | FileService | 文件操作 |
|
||||
| `frpService` | FRPService | 内网穿透 |
|
||||
| `giteaService` | GiteaService | Git 服务 |
|
||||
| `httpServer` | Server | HTTP 服务器 |
|
||||
| `wsServer` | WebSocketServer | WebSocket 服务器 |
|
||||
| `streamBroadcaster` | StreamBroadcaster | 流广播 |
|
||||
|
||||
#### 启动顺序
|
||||
|
||||
```javascript
|
||||
async start() {
|
||||
errorHandler.initialize();
|
||||
this._setupRoutes();
|
||||
|
||||
await httpServer.start();
|
||||
wsServer.start(httpServer.getHTTPServer());
|
||||
this._setupWebSocketHandlers();
|
||||
|
||||
ffmpegEncoder.start();
|
||||
streamBroadcaster.setEncoder(ffmpegEncoder);
|
||||
|
||||
await inputService.start();
|
||||
frpService.start();
|
||||
giteaService.start();
|
||||
|
||||
await this.eventBus.emit(EventTypes.APP_START, { ... });
|
||||
this._setupGracefulShutdown();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Container.js - 依赖注入容器
|
||||
|
||||
Container 是一个轻量级的依赖注入容器,负责服务的注册、解析和生命周期管理。
|
||||
|
||||
#### 核心功能
|
||||
|
||||
- **服务注册**: 使用工厂函数注册服务
|
||||
- **单例模式**: 默认所有服务为单例
|
||||
- **循环依赖检测**: 自动检测并阻止循环依赖
|
||||
|
||||
#### API 说明
|
||||
|
||||
```javascript
|
||||
class Container {
|
||||
register(name, factory, isSingleton = true)
|
||||
resolve(name)
|
||||
has(name)
|
||||
unregister(name)
|
||||
clear()
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```javascript
|
||||
const container = new Container();
|
||||
|
||||
container.register('config', (c) => {
|
||||
return require('./config');
|
||||
});
|
||||
|
||||
container.register('database', (c) => {
|
||||
const config = c.resolve('config');
|
||||
return new Database(config.db);
|
||||
});
|
||||
|
||||
container.register('userService', (c) => {
|
||||
const db = c.resolve('database');
|
||||
return new UserService(db);
|
||||
});
|
||||
|
||||
const userService = container.resolve('userService');
|
||||
```
|
||||
|
||||
#### 循环依赖检测
|
||||
|
||||
```javascript
|
||||
resolve(name) {
|
||||
if (this._resolving.has(name)) {
|
||||
const chain = Array.from(this._resolving).join(' -> ');
|
||||
throw new Error(
|
||||
`Circular dependency detected: ${chain} -> ${name}`
|
||||
);
|
||||
}
|
||||
|
||||
this._resolving.add(name);
|
||||
try {
|
||||
const instance = service.factory(this);
|
||||
return instance;
|
||||
} finally {
|
||||
this._resolving.delete(name);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.3 EventBus.js - 事件总线
|
||||
|
||||
EventBus 实现了发布-订阅模式,用于模块间的松耦合通信。
|
||||
|
||||
#### 核心功能
|
||||
|
||||
- **事件订阅**: `on(event, handler)` 订阅事件
|
||||
- **事件发布**: `emit(event, data)` 发布事件
|
||||
- **一次性订阅**: `once(event, handler)` 只触发一次
|
||||
- **取消订阅**: `off(event, handler)` 移除监听器
|
||||
|
||||
#### API 说明
|
||||
|
||||
```javascript
|
||||
class EventBus {
|
||||
on(event, handler)
|
||||
off(event, handler)
|
||||
emit(event, data)
|
||||
once(event, handler)
|
||||
removeAllListeners(event)
|
||||
listenerCount(event)
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用示例
|
||||
|
||||
```javascript
|
||||
const eventBus = new EventBus();
|
||||
|
||||
eventBus.on('user:login', async (data) => {
|
||||
console.log(`User ${data.userId} logged in`);
|
||||
});
|
||||
|
||||
await eventBus.emit('user:login', { userId: '123' });
|
||||
```
|
||||
|
||||
#### 事件类型
|
||||
|
||||
系统预定义的事件类型:
|
||||
|
||||
```javascript
|
||||
const EventTypes = {
|
||||
STREAM_START: 'stream:start',
|
||||
STREAM_STOP: 'stream:stop',
|
||||
STREAM_DATA: 'stream:data',
|
||||
STREAM_ERROR: 'stream:error',
|
||||
CLIENT_CONNECTED: 'client:connected',
|
||||
CLIENT_DISCONNECTED: 'client:disconnected',
|
||||
INPUT_EVENT: 'input:event',
|
||||
APP_START: 'app:start',
|
||||
APP_STOP: 'app:stop',
|
||||
ERROR: 'error'
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 ErrorHandler.js - 错误处理器
|
||||
|
||||
ErrorHandler 提供全局错误处理机制,捕获未处理的异常和 Promise 拒绝。
|
||||
|
||||
#### 核心功能
|
||||
|
||||
- **全局异常捕获**: 捕获 `uncaughtException`
|
||||
- **Promise 拒绝捕获**: 捕获 `unhandledRejection`
|
||||
- **错误响应生成**: 统一的错误响应格式
|
||||
|
||||
#### 初始化
|
||||
|
||||
```javascript
|
||||
class ErrorHandler {
|
||||
initialize() {
|
||||
process.on('uncaughtException', (error) => {
|
||||
logger.error('Uncaught Exception:', {
|
||||
message: error.message,
|
||||
stack: error.stack,
|
||||
name: error.name
|
||||
});
|
||||
setTimeout(() => process.exit(1), 1000);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
logger.error('Unhandled Rejection:', { reason });
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应格式
|
||||
|
||||
```javascript
|
||||
function createErrorResponse(error, code, details) {
|
||||
const response = {
|
||||
error: error instanceof Error ? error.message : String(error)
|
||||
};
|
||||
if (code) response.code = code;
|
||||
if (details !== undefined) response.details = details;
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 服务层设计说明
|
||||
|
||||
### 3.1 服务模块结构
|
||||
|
||||
```
|
||||
src/services/
|
||||
├── auth/ # 认证服务
|
||||
│ ├── AuthService.js # 认证逻辑
|
||||
│ └── TokenManager.js # JWT Token 管理
|
||||
├── clipboard/ # 剪贴板服务
|
||||
│ └── ClipboardService.js
|
||||
├── file/ # 文件服务
|
||||
│ └── FileService.js
|
||||
├── input/ # 输入控制服务
|
||||
│ ├── InputService.js # 输入接口
|
||||
│ └── PowerShellInput.js # PowerShell 实现
|
||||
├── network/ # 网络服务
|
||||
│ ├── FRPService.js # 内网穿透
|
||||
│ └── GiteaService.js # Git 服务
|
||||
└── stream/ # 流媒体服务
|
||||
├── FFmpegEncoder.js # FFmpeg 编码
|
||||
├── ScreenCapture.js # 屏幕捕获
|
||||
└── StreamService.js # 流管理
|
||||
```
|
||||
|
||||
### 3.2 各服务职责
|
||||
|
||||
| 服务模块 | 职责 | 关键方法 |
|
||||
|---------|------|---------|
|
||||
| **AuthService** | 用户密码验证 | `authenticate(password)` |
|
||||
| **TokenManager** | JWT 生成与验证 | `generateToken()`, `verifyToken()` |
|
||||
| **FFmpegEncoder** | 屏幕录制与编码 | `start()`, `stop()`, `getScreenResolution()` |
|
||||
| **PowerShellInput** | 模拟鼠标键盘输入 | `mouseMove()`, `mouseDown()`, `keyDown()` |
|
||||
| **ClipboardService** | 剪贴板读写 | `get()`, `set()` |
|
||||
| **FileService** | 文件系统操作 | `readFile()`, `writeFile()`, `listDir()` |
|
||||
| **FRPService** | 内网穿透代理 | `start()`, `stop()` |
|
||||
| **GiteaService** | Git 仓库管理 | `start()`, `stop()` |
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据流向说明
|
||||
|
||||
### 4.1 用户认证流程
|
||||
|
||||
```
|
||||
┌────────┐ ┌───────────┐ ┌────────────┐ ┌──────────────┐
|
||||
│ Browser│ │HTTPServer │ │AuthService │ │TokenManager │
|
||||
└───┬────┘ └─────┬─────┘ └─────┬──────┘ └──────┬───────┘
|
||||
│ │ │ │
|
||||
│ POST /login │ │ │
|
||||
│ {password: xxx} │ │ │
|
||||
│────────────────>│ │ │
|
||||
│ │ │ │
|
||||
│ │ authenticate() │ │
|
||||
│ │─────────────────>│ │
|
||||
│ │ │ │
|
||||
│ │ true/false │ │
|
||||
│ │<─────────────────│ │
|
||||
│ │ │ │
|
||||
│ │ generateToken() │
|
||||
│ │────────────────────────────────────────>
|
||||
│ │ │ │
|
||||
│ │ token │
|
||||
│ │<────────────────────────────────────────
|
||||
│ │ │ │
|
||||
│ Set-Cookie: auth=token; redirect / │ │
|
||||
│<────────────────│ │ │
|
||||
│ │ │ │
|
||||
```
|
||||
|
||||
### 4.2 实时控制流程
|
||||
|
||||
```
|
||||
┌────────┐ ┌──────────┐ ┌─────────────┐ ┌────────────────┐
|
||||
│ Browser│ │WSServer │ │InputHandler │ │PowerShellInput │
|
||||
└───┬────┘ └────┬─────┘ └──────┬──────┘ └───────┬────────┘
|
||||
│ │ │ │
|
||||
│ WebSocket │ │ │
|
||||
│ Connection │ │ │
|
||||
│─────────────>│ │ │
|
||||
│ │ │ │
|
||||
│ {type: │ │ │
|
||||
│ "mouseMove",│ │ │
|
||||
│ x: 100, │ │ │
|
||||
│ y: 200} │ │ │
|
||||
│─────────────>│ │ │
|
||||
│ │ │ │
|
||||
│ │ handleMessage() │ │
|
||||
│ │────────────────>│ │
|
||||
│ │ │ │
|
||||
│ │ │ mouseMove(100,200)│
|
||||
│ │ │──────────────────>│
|
||||
│ │ │ │
|
||||
│ │ │ 执行系统命令 │
|
||||
│ │ │ │
|
||||
```
|
||||
|
||||
### 4.3 视频流传输流程
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌───────────────┐ ┌──────────────────┐ ┌────────┐
|
||||
│ FFmpegEncoder│ │StreamBroadcaster│ │ WebSocketServer │ │ Browser│
|
||||
└──────┬───────┘ └───────┬───────┘ └────────┬─────────┘ └───┬────┘
|
||||
│ │ │ │
|
||||
│ 屏幕捕获 + 编码 │ │ │
|
||||
│ │ │ │
|
||||
│ video data │ │ │
|
||||
│───────────────────>│ │ │
|
||||
│ │ │ │
|
||||
│ │ broadcast to all │ │
|
||||
│ │ clients │ │
|
||||
│ │────────────────────>│ │
|
||||
│ │ │ │
|
||||
│ │ │ binary frame │
|
||||
│ │ │─────────────────>│
|
||||
│ │ │ │
|
||||
│ │ │ 渲染视频 │
|
||||
│ │ │ │
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. WebSocket 消息协议说明
|
||||
|
||||
### 5.1 消息格式
|
||||
|
||||
所有 WebSocket 消息使用 JSON 格式:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "messageType",
|
||||
...additionalFields
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 消息类型定义
|
||||
|
||||
```javascript
|
||||
const MessageTypes = {
|
||||
SCREEN_INFO: 'screenInfo',
|
||||
MOUSE_MOVE: 'mouseMove',
|
||||
MOUSE_DOWN: 'mouseDown',
|
||||
MOUSE_UP: 'mouseUp',
|
||||
MOUSE_WHEEL: 'mouseWheel',
|
||||
KEY_DOWN: 'keyDown',
|
||||
KEY_UP: 'keyUp',
|
||||
CLIPBOARD_GET: 'clipboardGet',
|
||||
CLIPBOARD_SET: 'clipboardSet',
|
||||
CLIPBOARD_DATA: 'clipboardData',
|
||||
CLIPBOARD_RESULT: 'clipboardResult',
|
||||
CLIPBOARD_TOO_LARGE: 'clipboardTooLarge'
|
||||
};
|
||||
```
|
||||
|
||||
### 5.3 消息详细说明
|
||||
|
||||
#### 屏幕信息 (screenInfo)
|
||||
|
||||
服务端发送屏幕分辨率信息:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "screenInfo",
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
```
|
||||
|
||||
#### 鼠标移动 (mouseMove)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "mouseMove",
|
||||
"x": 100,
|
||||
"y": 200
|
||||
}
|
||||
```
|
||||
|
||||
#### 鼠标按下/释放 (mouseDown/mouseUp)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "mouseDown",
|
||||
"button": 0
|
||||
}
|
||||
```
|
||||
|
||||
| button 值 | 说明 |
|
||||
|----------|------|
|
||||
| 0 | 左键 |
|
||||
| 1 | 中键 |
|
||||
| 2 | 右键 |
|
||||
|
||||
#### 鼠标滚轮 (mouseWheel)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "mouseWheel",
|
||||
"deltaX": 0,
|
||||
"deltaY": -120
|
||||
}
|
||||
```
|
||||
|
||||
#### 键盘按下/释放 (keyDown/keyUp)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "keyDown",
|
||||
"keyCode": 65,
|
||||
"key": "a"
|
||||
}
|
||||
```
|
||||
|
||||
#### 剪贴板获取 (clipboardGet)
|
||||
|
||||
客户端请求获取剪贴板内容:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "clipboardGet"
|
||||
}
|
||||
```
|
||||
|
||||
#### 剪贴板设置 (clipboardSet)
|
||||
|
||||
客户端设置剪贴板内容:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "clipboardSet",
|
||||
"data": "clipboard text content"
|
||||
}
|
||||
```
|
||||
|
||||
#### 剪贴板数据 (clipboardData)
|
||||
|
||||
服务端返回剪贴板数据:
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "clipboardData",
|
||||
"data": "clipboard text content"
|
||||
}
|
||||
```
|
||||
|
||||
#### 剪贴板操作结果 (clipboardResult)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "clipboardResult",
|
||||
"success": true
|
||||
}
|
||||
```
|
||||
|
||||
#### 剪贴板过大 (clipboardTooLarge)
|
||||
|
||||
```javascript
|
||||
{
|
||||
"type": "clipboardTooLarge",
|
||||
"size": 10485760,
|
||||
"maxSize": 5242880
|
||||
}
|
||||
```
|
||||
|
||||
### 5.4 消息处理流程
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ WebSocket 消息处理 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 客户端消息 ──> JSON 解析 ──> 类型判断 ──> 分发处理 ──> 执行操作 │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ InputHandler │ │
|
||||
│ ├──────────────────────────────────────────────────────────┤ │
|
||||
│ │ mouseMove ──> inputService.mouseMove(x, y) │ │
|
||||
│ │ mouseDown ──> inputService.mouseDown(button) │ │
|
||||
│ │ mouseUp ──> inputService.mouseUp(button) │ │
|
||||
│ │ mouseWheel ──> inputService.mouseWheel(deltaX, deltaY) │ │
|
||||
│ │ keyDown ──> inputService.keyDown(keyCode) │ │
|
||||
│ │ keyUp ──> inputService.keyUp(keyCode) │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||||
│ │ ClipboardService │ │
|
||||
│ ├──────────────────────────────────────────────────────────┤ │
|
||||
│ │ clipboardGet ──> clipboardService.get() ──> 返回数据 │ │
|
||||
│ │ clipboardSet ──> clipboardService.set(data) ──> 确认 │ │
|
||||
│ └──────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 设计原则总结
|
||||
|
||||
### 6.1 SOLID 原则应用
|
||||
|
||||
| 原则 | 应用场景 |
|
||||
|------|---------|
|
||||
| **单一职责** | 每个服务类只负责一个特定功能 |
|
||||
| **开放封闭** | 通过 Container 注册新服务,无需修改核心代码 |
|
||||
| **依赖倒置** | 高层模块依赖抽象接口,具体实现通过 Container 注入 |
|
||||
|
||||
### 6.2 最佳实践
|
||||
|
||||
1. **依赖注入**: 所有服务通过 Container 获取,避免硬编码依赖
|
||||
2. **事件驱动**: 模块间通过 EventBus 通信,降低耦合度
|
||||
3. **优雅关闭**: 正确处理系统信号,确保资源释放
|
||||
4. **错误处理**: 全局捕获未处理异常,记录日志并安全退出
|
||||
5. **日志记录**: 统一的日志格式,便于问题排查
|
||||
240
remote/docs/开发/部署指南.md
Normal file
240
remote/docs/开发/部署指南.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# 部署指南
|
||||
|
||||
本文档介绍远程屏幕监控系统的开发环境运行、生产环境部署、pkg 打包和 Windows 服务安装。
|
||||
|
||||
---
|
||||
|
||||
## 一、开发环境运行
|
||||
|
||||
### 1.1 环境要求
|
||||
|
||||
| 软件 | 版本要求 | 说明 |
|
||||
|------|----------|------|
|
||||
| Node.js | >= 16.0.0 | 推荐使用 LTS 版本 |
|
||||
| 操作系统 | Windows | 输入控制功能需要 Windows 系统 |
|
||||
| FFmpeg | 内置 | 通过 npm 自动安装 |
|
||||
|
||||
### 1.2 安装依赖
|
||||
|
||||
```powershell
|
||||
# 进入项目目录
|
||||
cd C:\Users\xuanchi\Desktop\remote
|
||||
|
||||
# 安装依赖
|
||||
npm install
|
||||
```
|
||||
|
||||
### 1.3 启动开发服务器
|
||||
|
||||
```powershell
|
||||
# 方式一:使用 dev 脚本
|
||||
npm run dev
|
||||
|
||||
# 方式二:使用 start 脚本
|
||||
npm start
|
||||
|
||||
# 方式三:直接运行
|
||||
node src/index.js
|
||||
```
|
||||
|
||||
启动成功后,访问 http://localhost:3000 查看屏幕流。
|
||||
|
||||
---
|
||||
|
||||
## 二、生产环境部署
|
||||
|
||||
### 2.1 环境变量配置
|
||||
|
||||
所有配置项都可以通过环境变量覆盖,格式为 `REMOTE_<SECTION>_<KEY>`:
|
||||
|
||||
```powershell
|
||||
# 服务器配置
|
||||
REMOTE_SERVER_PORT=3000
|
||||
REMOTE_SERVER_HOST=0.0.0.0
|
||||
|
||||
# 安全配置
|
||||
REMOTE_SECURITY_PASSWORD=your_password
|
||||
REMOTE_SECURITY_TOKENEXPIRY=3600
|
||||
|
||||
# JWT 密钥
|
||||
JWT_SECRET=your_jwt_secret
|
||||
|
||||
# 流媒体配置
|
||||
REMOTE_STREAM_FPS=30
|
||||
REMOTE_STREAM_BITRATE=4000k
|
||||
|
||||
# 输入控制配置
|
||||
REMOTE_INPUT_MOUSEENABLED=true
|
||||
REMOTE_INPUT_KEYBOARDENABLED=true
|
||||
|
||||
# FRP 配置
|
||||
REMOTE_FRP_ENABLED=true
|
||||
```
|
||||
|
||||
### 2.2 配置文件修改
|
||||
|
||||
配置文件位于 `config/default.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"port": 3000,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"stream": {
|
||||
"fps": 30,
|
||||
"bitrate": "4000k",
|
||||
"gop": 10,
|
||||
"preset": "ultrafast",
|
||||
"resolution": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"mouseEnabled": true,
|
||||
"keyboardEnabled": true,
|
||||
"sensitivity": 1.0
|
||||
},
|
||||
"security": {
|
||||
"password": "",
|
||||
"tokenExpiry": 3600
|
||||
},
|
||||
"frp": {
|
||||
"enabled": true,
|
||||
"frpcPath": "./frp/frpc.exe",
|
||||
"configPath": "./frp/frpc.toml"
|
||||
},
|
||||
"gitea": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 启动生产服务
|
||||
|
||||
```powershell
|
||||
# 设置环境变量
|
||||
$env:NODE_ENV="production"
|
||||
$env:REMOTE_SECURITY_PASSWORD="your_password"
|
||||
|
||||
# 启动服务
|
||||
node src/index.js
|
||||
```
|
||||
|
||||
或创建启动脚本 `start.ps1`:
|
||||
|
||||
```powershell
|
||||
$env:NODE_ENV="production"
|
||||
$env:REMOTE_SECURITY_PASSWORD="your_password"
|
||||
$env:JWT_SECRET="your_jwt_secret"
|
||||
|
||||
node src/index.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、pkg 打包说明
|
||||
|
||||
### 3.1 package.json 中的 pkg 配置
|
||||
|
||||
```json
|
||||
{
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"node_modules/@ffmpeg-installer/**/*"
|
||||
],
|
||||
"scripts": [
|
||||
"src/**/*.js"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 配置项 | 说明 |
|
||||
|--------|------|
|
||||
| `assets` | 需要打包的资源文件,包含 FFmpeg 二进制文件 |
|
||||
| `scripts` | 需要打包的脚本文件 |
|
||||
|
||||
### 3.2 打包命令
|
||||
|
||||
```powershell
|
||||
# 执行打包
|
||||
npm run build
|
||||
```
|
||||
|
||||
打包命令实际执行:
|
||||
|
||||
```powershell
|
||||
# 1. 使用 pkg 打包为可执行文件
|
||||
pkg . --targets node18-win-x64 --output dist/remote-screen-monitor.exe
|
||||
|
||||
# 2. 复制资源文件
|
||||
npm run build:copy-assets
|
||||
```
|
||||
|
||||
### 3.3 打包输出目录
|
||||
|
||||
打包后的文件位于 `dist/` 目录:
|
||||
|
||||
```
|
||||
dist/
|
||||
├── remote-screen-monitor.exe # 主程序
|
||||
├── public/ # 前端静态文件
|
||||
│ ├── css/
|
||||
│ ├── js/
|
||||
│ └── index.html
|
||||
├── config/ # 配置文件
|
||||
│ └── default.json
|
||||
├── frp/ # FRP 客户端
|
||||
│ ├── frpc.exe
|
||||
│ └── frpc.toml
|
||||
└── ffmpeg.exe # FFmpeg 编码器
|
||||
```
|
||||
|
||||
### 3.4 资源文件处理
|
||||
|
||||
打包时需要手动复制的资源:
|
||||
|
||||
| 资源 | 来源 | 目标 |
|
||||
|------|------|------|
|
||||
| 前端文件 | `public/` | `dist/public/` |
|
||||
| 配置文件 | `config/` | `dist/config/` |
|
||||
| FRP 客户端 | `frp/` | `dist/frp/` |
|
||||
| FFmpeg | `node_modules/@ffmpeg-installer/win32-x64/ffmpeg.exe` | `dist/ffmpeg.exe` |
|
||||
|
||||
---
|
||||
|
||||
## 四、NSSM Windows 服务安装
|
||||
|
||||
### 4.1 服务安装命令
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行 PowerShell
|
||||
cd C:\Users\xuanchi\Desktop\remote\nssm
|
||||
|
||||
# 安装服务(使用打包后的 exe)
|
||||
.\nssm.exe install RemoteApp "C:\path\to\dist\remote-screen-monitor.exe"
|
||||
|
||||
# 或使用 Node.js 运行
|
||||
.\nssm.exe install RemoteApp "C:\Program Files\nodejs\node.exe" "C:\Users\xuanchi\Desktop\remote\src\index.js"
|
||||
```
|
||||
|
||||
### 4.2 服务配置
|
||||
|
||||
```powershell
|
||||
# 设置工作目录
|
||||
.\nssm.exe set RemoteApp AppDirectory "C:\path\to\app"
|
||||
|
||||
# 设置显示名称
|
||||
.\nssm.exe set RemoteApp DisplayName "Remote Desktop Application"
|
||||
|
||||
# 设置描述
|
||||
.\nssm.exe set RemoteApp Description "Remote screen streaming service"
|
||||
|
||||
# 设置启动类型(自动启动)
|
||||
.\nssm.exe set RemoteApp Start SERVICE_AUTO_START
|
||||
|
||||
# 设置日志输出
|
||||
.\nssm.exe set RemoteApp AppStdout "C:\path\to\app\logs\service.log"
|
||||
.\nssm.exe set RemoteApp AppStderr "C:\path\to\
|
||||
569
remote/docs/开发/配置指南.md
Normal file
569
remote/docs/开发/配置指南.md
Normal file
@@ -0,0 +1,569 @@
|
||||
# 配置指南
|
||||
|
||||
本文档详细说明 Remote 项目的配置系统,包括配置文件结构、配置项说明、环境变量覆盖机制以及配置验证规则。
|
||||
|
||||
## 目录
|
||||
|
||||
- [配置文件结构](#配置文件结构)
|
||||
- [配置项详细说明](#配置项详细说明)
|
||||
- [server 配置](#server-配置)
|
||||
- [stream 配置](#stream-配置)
|
||||
- [input 配置](#input-配置)
|
||||
- [security 配置](#security-配置)
|
||||
- [frp 配置](#frp-配置)
|
||||
- [gitea 配置](#gitea-配置)
|
||||
- [环境变量覆盖机制](#环境变量覆盖机制)
|
||||
- [配置验证规则](#配置验证规则)
|
||||
- [不同环境配置示例](#不同环境配置示例)
|
||||
|
||||
---
|
||||
|
||||
## 配置文件结构
|
||||
|
||||
配置文件位于 `config/default.json`,采用 JSON 格式,包含以下顶级配置节:
|
||||
|
||||
```json
|
||||
{
|
||||
"server": { ... },
|
||||
"stream": { ... },
|
||||
"input": { ... },
|
||||
"security": { ... },
|
||||
"frp": { ... },
|
||||
"gitea": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
配置加载优先级(从低到高):
|
||||
|
||||
1. **默认值** - `schema.js` 中定义的 `defaultConfig`
|
||||
2. **配置文件** - `config/default.json`
|
||||
3. **环境变量** - `REMOTE_<SECTION>_<KEY>` 格式的环境变量
|
||||
|
||||
---
|
||||
|
||||
## 配置项详细说明
|
||||
|
||||
### server 配置
|
||||
|
||||
服务器基本配置,控制 HTTP 服务的监听地址和端口。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 说明 |
|
||||
|--------|------|--------|------|------|
|
||||
| `port` | number | 3000 | 是 | 服务监听端口,范围 1-65535 |
|
||||
| `host` | string | "0.0.0.0" | 是 | 服务监听地址,`0.0.0.0` 表示监听所有网卡 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"port": 3000,
|
||||
"host": "0.0.0.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**使用场景:**
|
||||
|
||||
- 开发环境:使用默认端口 3000
|
||||
- 生产环境:可使用 80 或 443(需要管理员权限)
|
||||
- 局域网访问:`host` 设置为 `0.0.0.0`
|
||||
- 仅本机访问:`host` 设置为 `127.0.0.1`
|
||||
|
||||
---
|
||||
|
||||
### stream 配置
|
||||
|
||||
视频流编码配置,控制远程桌面的画面质量和性能。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 约束 | 说明 |
|
||||
|--------|------|--------|------|------|------|
|
||||
| `fps` | number | 30 | 是 | 1-120 | 帧率,每秒传输帧数 |
|
||||
| `bitrate` | string | "4000k" | 是 | - | 码率,控制视频质量,如 `2000k`、`4000k`、`8000k` |
|
||||
| `gop` | number | 10 | 是 | ≥1 | GOP 大小(关键帧间隔),影响延迟和压缩效率 |
|
||||
| `preset` | string | "ultrafast" | 是 | - | 编码预设,影响编码速度和压缩效率 |
|
||||
| `resolution.width` | number | 1920 | 是 | ≥1 | 视频宽度(像素) |
|
||||
| `resolution.height` | number | 1080 | 是 | ≥1 | 视频高度(像素) |
|
||||
|
||||
**编码预设说明:**
|
||||
|
||||
| 预设值 | 编码速度 | 压缩效率 | 适用场景 |
|
||||
|--------|----------|----------|----------|
|
||||
| `ultrafast` | 最快 | 最低 | 低延迟场景,CPU 性能有限 |
|
||||
| `superfast` | 很快 | 较低 | 实时流媒体 |
|
||||
| `veryfast` | 快 | 一般 | 平衡性能和质量 |
|
||||
| `faster` | 较快 | 较好 | 一般用途 |
|
||||
| `fast` | 中等偏快 | 好 | 推荐设置 |
|
||||
| `medium` | 中等 | 很好 | 默认推荐 |
|
||||
| `slow` | 慢 | 很好 | 离线编码 |
|
||||
| `slower` | 很慢 | 极好 | 高质量要求 |
|
||||
| `veryslow` | 最慢 | 最好 | 最高质量,非实时 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"stream": {
|
||||
"fps": 30,
|
||||
"bitrate": "4000k",
|
||||
"gop": 10,
|
||||
"preset": "ultrafast",
|
||||
"resolution": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**性能调优建议:**
|
||||
|
||||
- **低带宽环境**:降低 `bitrate` 至 `2000k`,降低 `resolution` 至 1280x720
|
||||
- **高帧率需求**:提高 `fps` 至 60,同时提高 `bitrate`
|
||||
- **低延迟优先**:使用 `ultrafast` 预设,减小 `gop` 值
|
||||
- **画质优先**:使用 `medium` 或 `slow` 预设,提高 `bitrate`
|
||||
|
||||
---
|
||||
|
||||
### input 配置
|
||||
|
||||
输入设备控制配置,管理鼠标和键盘的远程输入。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 约束 | 说明 |
|
||||
|--------|------|--------|------|------|------|
|
||||
| `mouseEnabled` | boolean | true | 是 | - | 是否启用鼠标控制 |
|
||||
| `keyboardEnabled` | boolean | true | 是 | - | 是否启用键盘控制 |
|
||||
| `sensitivity` | number | 1.0 | 是 | 0.1-10 | 鼠标灵敏度,1.0 为标准灵敏度 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"input": {
|
||||
"mouseEnabled": true,
|
||||
"keyboardEnabled": true,
|
||||
"sensitivity": 1.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**使用场景:**
|
||||
|
||||
- **演示模式**:禁用鼠标和键盘输入,仅允许观看
|
||||
- **高精度操作**:降低灵敏度至 0.5
|
||||
- **快速操作**:提高灵敏度至 2.0
|
||||
|
||||
---
|
||||
|
||||
### security 配置
|
||||
|
||||
安全认证配置,控制访问权限和会话管理。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 约束 | 说明 |
|
||||
|--------|------|--------|------|------|------|
|
||||
| `password` | string | "" | 否 | - | 访问密码,为空表示无需密码 |
|
||||
| `tokenExpiry` | number | 3600 | 是 | ≥60 | Token 有效期(秒),默认 1 小时 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"security": {
|
||||
"password": "your-secure-password",
|
||||
"tokenExpiry": 3600
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**安全建议:**
|
||||
|
||||
- 生产环境务必设置强密码
|
||||
- Token 有效期建议设置为 1800-7200 秒
|
||||
- 敏感环境可缩短 Token 有效期至 900 秒
|
||||
|
||||
---
|
||||
|
||||
### frp 配置
|
||||
|
||||
FRP 内网穿透配置,用于通过公网访问内网服务。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 说明 |
|
||||
|--------|------|--------|------|------|
|
||||
| `enabled` | boolean | true | 是 | 是否启用 FRP 功能 |
|
||||
| `frpcPath` | string | "./frp/frpc.exe" | 否 | frpc 客户端路径 |
|
||||
| `configPath` | string | "./frp/frpc.toml" | 否 | frpc 配置文件路径 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"frp": {
|
||||
"enabled": true,
|
||||
"frpcPath": "./frp/frpc.exe",
|
||||
"configPath": "./frp/frpc.toml"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### gitea 配置
|
||||
|
||||
Gitea 集成配置,用于代码仓库管理。
|
||||
|
||||
| 配置项 | 类型 | 默认值 | 必填 | 说明 |
|
||||
|--------|------|--------|------|------|
|
||||
| `enabled` | boolean | true | 是 | 是否启用 Gitea 集成 |
|
||||
|
||||
**配置示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"gitea": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 环境变量覆盖机制
|
||||
|
||||
配置系统支持通过环境变量覆盖配置文件中的值,环境变量优先级最高。
|
||||
|
||||
### 命名规则
|
||||
|
||||
环境变量采用 `REMOTE_<SECTION>_<KEY>` 格式:
|
||||
|
||||
```
|
||||
REMOTE_<配置节>_<配置项>
|
||||
```
|
||||
|
||||
- 所有字母大写
|
||||
- 使用下划线 `_` 分隔
|
||||
- 支持多层嵌套配置
|
||||
|
||||
### 类型自动转换
|
||||
|
||||
系统会自动识别并转换环境变量的类型:
|
||||
|
||||
| 环境变量值 | 转换结果 |
|
||||
|------------|----------|
|
||||
| `"true"` | `true` (boolean) |
|
||||
| `"false"` | `false` (boolean) |
|
||||
| `"123"` | `123` (number) |
|
||||
| `"123.45"` | `123.45` (number) |
|
||||
| 其他值 | 原始字符串 |
|
||||
|
||||
### 环境变量示例
|
||||
|
||||
| 环境变量 | 对应配置 | 说明 |
|
||||
|----------|----------|------|
|
||||
| `REMOTE_SERVER_PORT=8080` | `server.port = 8080` | 修改服务端口 |
|
||||
| `REMOTE_SERVER_HOST=127.0.0.1` | `server.host = "127.0.0.1"` | 修改监听地址 |
|
||||
| `REMOTE_STREAM_FPS=60` | `stream.fps = 60` | 修改帧率 |
|
||||
| `REMOTE_STREAM_BITRATE=8000k` | `stream.bitrate = "8000k"` | 修改码率 |
|
||||
| `REMOTE_INPUT_MOUSEENABLED=false` | `input.mouseEnabled = false` | 禁用鼠标 |
|
||||
| `REMOTE_INPUT_SENSITIVITY=2.0` | `input.sensitivity = 2.0` | 修改灵敏度 |
|
||||
| `REMOTE_SECURITY_PASSWORD=secret123` | `security.password = "secret123"` | 设置密码 |
|
||||
| `REMOTE_SECURITY_TOKENEXPIRY=7200` | `security.tokenExpiry = 7200` | Token 有效期 2 小时 |
|
||||
| `REMOTE_FRP_ENABLED=false` | `frp.enabled = false` | 禁用 FRP |
|
||||
| `REMOTE_GITEA_ENABLED=false` | `gitea.enabled = false` | 禁用 Gitea |
|
||||
|
||||
### 嵌套配置的环境变量
|
||||
|
||||
对于嵌套配置(如 `resolution.width`),使用下划线连接:
|
||||
|
||||
```bash
|
||||
REMOTE_STREAM_RESOLUTION_WIDTH=1280
|
||||
REMOTE_STREAM_RESOLUTION_HEIGHT=720
|
||||
```
|
||||
|
||||
对应配置:
|
||||
|
||||
```json
|
||||
{
|
||||
"stream": {
|
||||
"resolution": {
|
||||
"width": 1280,
|
||||
"height": 720
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置验证规则
|
||||
|
||||
配置系统通过 `schema.js` 定义验证规则,在加载配置时自动执行验证。
|
||||
|
||||
### 验证流程
|
||||
|
||||
```
|
||||
加载配置 → 合并默认值 → 应用环境变量 → 执行验证 → 返回验证结果
|
||||
```
|
||||
|
||||
### 验证规则详解
|
||||
|
||||
#### 类型验证
|
||||
|
||||
| 类型 | 验证规则 |
|
||||
|------|----------|
|
||||
| `number` | 必须是有效数字,非 NaN |
|
||||
| `string` | 必须是字符串类型 |
|
||||
| `boolean` | 必须是布尔类型 |
|
||||
| `object` | 必须是非 null 对象,非数组 |
|
||||
|
||||
#### 数值约束
|
||||
|
||||
| 约束 | 说明 |
|
||||
|------|------|
|
||||
| `min` | 最小值(包含) |
|
||||
| `max` | 最大值(包含) |
|
||||
|
||||
#### 必填验证
|
||||
|
||||
| 属性 | 说明 |
|
||||
|------|------|
|
||||
| `required: true` | 必须提供该配置项 |
|
||||
| `required: false` | 可选配置项 |
|
||||
|
||||
### 完整验证规则表
|
||||
|
||||
| 配置项 | 类型 | 必填 | 最小值 | 最大值 |
|
||||
|--------|------|------|--------|--------|
|
||||
| `server.port` | number | 是 | 1 | 65535 |
|
||||
| `server.host` | string | 是 | - | - |
|
||||
| `stream.fps` | number | 是 | 1 | 120 |
|
||||
| `stream.bitrate` | string | 是 | - | - |
|
||||
| `stream.gop` | number | 是 | 1 | - |
|
||||
| `stream.preset` | string | 是 | - | - |
|
||||
| `stream.resolution.width` | number | 是 | 1 | - |
|
||||
| `stream.resolution.height` | number | 是 | 1 | - |
|
||||
| `input.mouseEnabled` | boolean | 是 | - | - |
|
||||
| `input.keyboardEnabled` | boolean | 是 | - | - |
|
||||
| `input.sensitivity` | number | 是 | 0.1 | 10 |
|
||||
| `security.password` | string | 否 | - | - |
|
||||
| `security.tokenExpiry` | number | 是 | 60 | - |
|
||||
| `frp.enabled` | boolean | 是 | - | - |
|
||||
| `gitea.enabled` | boolean | 是 | - | - |
|
||||
|
||||
### 验证错误处理
|
||||
|
||||
当配置验证失败时,系统会:
|
||||
|
||||
1. 收集所有验证错误
|
||||
2. 输出警告日志
|
||||
3. 继续使用配置(不会阻止启动)
|
||||
|
||||
**验证错误示例:**
|
||||
|
||||
```javascript
|
||||
// 配置验证警告输出示例
|
||||
Config validation warnings: [
|
||||
'stream.fps: value 150 is greater than maximum 120',
|
||||
'input.sensitivity: value 0.05 is less than minimum 0.1',
|
||||
'server.port: expected type number, got string'
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 不同环境配置示例
|
||||
|
||||
### 开发环境配置
|
||||
|
||||
适用于本地开发和测试,注重调试便利性。
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"port": 3000,
|
||||
"host": "127.0.0.1"
|
||||
},
|
||||
"stream": {
|
||||
"fps": 30,
|
||||
"bitrate": "2000k",
|
||||
"gop": 10,
|
||||
"preset": "ultrafast",
|
||||
"resolution": {
|
||||
"width": 1280,
|
||||
"height": 720
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"mouseEnabled": true,
|
||||
"keyboardEnabled": true,
|
||||
"sensitivity": 1.0
|
||||
},
|
||||
"security": {
|
||||
"password": "",
|
||||
"tokenExpiry": 86400
|
||||
},
|
||||
"frp": {
|
||||
"enabled": false
|
||||
},
|
||||
"gitea": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**开发环境特点:**
|
||||
|
||||
- 仅本机访问(`127.0.0.1`)
|
||||
- 较低码率和分辨率,节省资源
|
||||
- 无密码验证,便于快速测试
|
||||
- 禁用 FRP 和 Gitea,减少依赖
|
||||
|
||||
### 生产环境配置
|
||||
|
||||
适用于正式部署,注重安全性和性能。
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"port": 443,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"stream": {
|
||||
"fps": 60,
|
||||
"bitrate": "8000k",
|
||||
"gop": 30,
|
||||
"preset": "fast",
|
||||
"resolution": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"mouseEnabled": true,
|
||||
"keyboardEnabled": true,
|
||||
"sensitivity": 1.0
|
||||
},
|
||||
"security": {
|
||||
"password": "your-strong-password-here",
|
||||
"tokenExpiry": 1800
|
||||
},
|
||||
"frp": {
|
||||
"enabled": true,
|
||||
"frpcPath": "/opt/frp/frpc",
|
||||
"configPath": "/etc/frp/frpc.toml"
|
||||
},
|
||||
"gitea": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**生产环境特点:**
|
||||
|
||||
- 监听所有网卡,支持外网访问
|
||||
- 高帧率和高码率,保证画质
|
||||
- 强密码保护,短 Token 有效期
|
||||
- 启用 FRP 内网穿透和 Gitea 集成
|
||||
|
||||
### 低带宽环境配置
|
||||
|
||||
适用于网络带宽受限的场景。
|
||||
|
||||
```json
|
||||
{
|
||||
"server": {
|
||||
"port": 3000,
|
||||
"host": "0.0.0.0"
|
||||
},
|
||||
"stream": {
|
||||
"fps": 15,
|
||||
"bitrate": "1000k",
|
||||
"gop": 30,
|
||||
"preset": "ultrafast",
|
||||
"resolution": {
|
||||
"width": 854,
|
||||
"height": 480
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"mouseEnabled": true,
|
||||
"keyboardEnabled": true,
|
||||
"sensitivity": 1.0
|
||||
},
|
||||
"security": {
|
||||
"password": "secure-password",
|
||||
"tokenExpiry": 3600
|
||||
},
|
||||
"frp": {
|
||||
"enabled": true
|
||||
},
|
||||
"gitea": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**低带宽环境特点:**
|
||||
|
||||
- 低帧率(15fps)和低码率(1000k)
|
||||
- 较小分辨率(480p)
|
||||
- 使用 `ultrafast` 预设减少编码延迟
|
||||
- 较大的 GOP 值提高压缩效率
|
||||
|
||||
### 环境变量配置示例
|
||||
|
||||
通过环境变量快速切换配置:
|
||||
|
||||
```bash
|
||||
# Windows (PowerShell)
|
||||
$env:REMOTE_SERVER_PORT=8080
|
||||
$env:REMOTE_STREAM_FPS=60
|
||||
$env:REMOTE_SECURITY_PASSWORD="production-password"
|
||||
|
||||
# Linux/macOS
|
||||
export REMOTE_SERVER_PORT=8080
|
||||
export REMOTE_STREAM_FPS=60
|
||||
export REMOTE_SECURITY_PASSWORD="production-password"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 配置 API
|
||||
|
||||
在代码中使用配置:
|
||||
|
||||
```javascript
|
||||
const config = require('./config');
|
||||
|
||||
// 获取整个配置
|
||||
const allConfig = config.getAll();
|
||||
|
||||
// 获取特定配置节
|
||||
const serverConfig = config.getSection('server');
|
||||
|
||||
// 获取单个配置项(支持点号分隔的路径)
|
||||
const port = config.get('server.port');
|
||||
const width = config.get('stream.resolution.width', 1920);
|
||||
|
||||
// 重新加载配置
|
||||
config.reload();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q: 配置修改后如何生效?
|
||||
|
||||
A: 需要重启服务或调用 `config.reload()` 方法。
|
||||
|
||||
### Q: 环境变量优先级为什么最高?
|
||||
|
||||
A: 为了支持容器化部署和 CI/CD 场景,允许在不修改配置文件的情况下动态调整配置。
|
||||
|
||||
### Q: 验证失败会阻止服务启动吗?
|
||||
|
||||
A: 不会,验证失败只会输出警告日志,服务仍会继续运行。
|
||||
|
||||
### Q: 如何查看当前生效的配置?
|
||||
|
||||
A: 可以通过 API 调用 `config.getAll()` 获取合并后的完整配置。
|
||||
389
remote/docs/指南/NSSM使用指南.md
Normal file
389
remote/docs/指南/NSSM使用指南.md
Normal file
@@ -0,0 +1,389 @@
|
||||
# NSSM 使用指南
|
||||
|
||||
> NSSM (Non-Sucking Service Manager) 是一个将任意应用程序安装为 Windows 服务的工具。
|
||||
|
||||
---
|
||||
|
||||
## 一、NSSM 简介
|
||||
|
||||
NSSM 可以将 Node.js 应用、Python 脚本、批处理文件等任何可执行程序安装为 Windows 服务,实现开机自启动和自动重启。
|
||||
|
||||
### 本地文件位置
|
||||
|
||||
```
|
||||
nssm/
|
||||
└── nssm.exe # 64 位版本
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、安装服务
|
||||
|
||||
### 基本语法
|
||||
|
||||
```powershell
|
||||
nssm install <服务名> <程序路径> [参数]
|
||||
```
|
||||
|
||||
### 安装 Remote 应用服务
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行 PowerShell
|
||||
cd C:\Users\xuanchi\Desktop\remote\nssm
|
||||
|
||||
# 安装服务
|
||||
.\nssm.exe install RemoteApp "C:\Program Files\nodejs\node.exe" "C:\Users\xuanchi\Desktop\remote\index.js"
|
||||
```
|
||||
|
||||
### 使用图形界面安装
|
||||
|
||||
```powershell
|
||||
# 打开图形界面
|
||||
.\nssm.exe install RemoteApp
|
||||
```
|
||||
|
||||
在弹出的窗口中配置:
|
||||
|
||||
| 标签页 | 配置项 | 值 |
|
||||
|--------|--------|-----|
|
||||
| Application | Path | `C:\Program Files\nodejs\node.exe` |
|
||||
| Application | Startup directory | `C:\Users\xuanchi\Desktop\remote` |
|
||||
| Application | Arguments | `index.js` |
|
||||
| I/O | Output (stdout) | `C:\Users\xuanchi\Desktop\remote\logs\service.log` |
|
||||
| I/O | Error (stderr) | `C:\Users\xuanchi\Desktop\remote\logs\error.log` |
|
||||
|
||||
---
|
||||
|
||||
## 三、管理服务
|
||||
|
||||
### 启动服务
|
||||
|
||||
```powershell
|
||||
# 通过 NSSM
|
||||
.\nssm.exe start RemoteApp
|
||||
|
||||
# 或通过 Windows 命令
|
||||
net start RemoteApp
|
||||
|
||||
# 或通过 sc 命令
|
||||
sc start RemoteApp
|
||||
```
|
||||
|
||||
### 停止服务
|
||||
|
||||
```powershell
|
||||
# 通过 NSSM
|
||||
.\nssm.exe stop RemoteApp
|
||||
|
||||
# 或通过 Windows 命令
|
||||
net stop RemoteApp
|
||||
|
||||
# 或通过 sc 命令
|
||||
sc stop RemoteApp
|
||||
```
|
||||
|
||||
### 重启服务
|
||||
|
||||
```powershell
|
||||
.\nssm.exe restart RemoteApp
|
||||
```
|
||||
|
||||
### 查看服务状态
|
||||
|
||||
```powershell
|
||||
.\nssm.exe status RemoteApp
|
||||
```
|
||||
|
||||
### 编辑服务配置
|
||||
|
||||
```powershell
|
||||
# 打开图形界面编辑
|
||||
.\nssm.exe edit RemoteApp
|
||||
```
|
||||
|
||||
### 删除服务
|
||||
|
||||
```powershell
|
||||
# 先停止服务
|
||||
.\nssm.exe stop RemoteApp
|
||||
|
||||
# 删除服务
|
||||
.\nssm.exe remove RemoteApp
|
||||
|
||||
# 确认删除(不弹窗)
|
||||
.\nssm.exe remove RemoteApp confirm
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、完整安装示例
|
||||
|
||||
### 一键安装脚本
|
||||
|
||||
创建 `install-service.ps1`:
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行
|
||||
$nssmPath = "C:\Users\xuanchi\Desktop\remote\nssm\nssm.exe"
|
||||
$nodePath = "C:\Program Files\nodejs\node.exe"
|
||||
$appPath = "C:\Users\xuanchi\Desktop\remote"
|
||||
$logPath = "C:\Users\xuanchi\Desktop\remote\logs"
|
||||
|
||||
# 创建日志目录
|
||||
if (-not (Test-Path $logPath)) {
|
||||
New-Item -ItemType Directory -Path $logPath
|
||||
}
|
||||
|
||||
# 安装服务
|
||||
& $nssmPath install RemoteApp $nodePath "index.js"
|
||||
|
||||
# 设置工作目录
|
||||
& $nssmPath set RemoteApp AppDirectory $appPath
|
||||
|
||||
# 设置显示名称
|
||||
& $nssmPath set RemoteApp DisplayName "Remote Desktop Application"
|
||||
|
||||
# 设置描述
|
||||
& $nssmPath set RemoteApp Description "Remote screen streaming service"
|
||||
|
||||
# 设置启动类型(自动)
|
||||
& $nssmPath set RemoteApp Start SERVICE_AUTO_START
|
||||
|
||||
# 设置日志输出
|
||||
& $nssmPath set RemoteApp AppStdout "$logPath\service.log"
|
||||
& $nssmPath set RemoteApp AppStderr "$logPath\error.log"
|
||||
|
||||
# 设置日志轮转
|
||||
& $nssmPath set RemoteApp AppRotateFiles 1
|
||||
& $nssmPath set RemoteApp AppRotateBytes 1048576
|
||||
|
||||
# 设置失败重启
|
||||
& $nssmPath set RemoteApp AppExit Default Restart
|
||||
& $nssmPath set RemoteApp AppRestartDelay 5000
|
||||
|
||||
# 启动服务
|
||||
& $nssmPath start RemoteApp
|
||||
|
||||
Write-Host "RemoteApp service installed and started successfully!"
|
||||
```
|
||||
|
||||
### 运行安装脚本
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行
|
||||
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
|
||||
.\install-service.ps1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、高级配置
|
||||
|
||||
### 设置环境变量
|
||||
|
||||
```powershell
|
||||
.\nssm.exe set RemoteApp AppEnvironmentExtra "NODE_ENV=production" "PORT=3000"
|
||||
```
|
||||
|
||||
### 设置服务依赖
|
||||
|
||||
```powershell
|
||||
# 依赖其他服务(如需要网络)
|
||||
.\nssm.exe set RemoteApp DependOnService Tcpip
|
||||
```
|
||||
|
||||
### 设置用户账户
|
||||
|
||||
```powershell
|
||||
# 使用特定用户运行
|
||||
.\nssm.exe set RemoteApp ObjectName ".\username" "password"
|
||||
```
|
||||
|
||||
### 设置优先级
|
||||
|
||||
```powershell
|
||||
# 设置进程优先级
|
||||
.\nssm.exe set RemoteApp AppPriority NORMAL_PRIORITY_CLASS
|
||||
|
||||
# 可选值:
|
||||
# IDLE_PRIORITY_CLASS
|
||||
# BELOW_NORMAL_PRIORITY_CLASS
|
||||
# NORMAL_PRIORITY_CLASS
|
||||
# ABOVE_NORMAL_PRIORITY_CLASS
|
||||
# HIGH_PRIORITY_CLASS
|
||||
# REALTIME_PRIORITY_CLASS
|
||||
```
|
||||
|
||||
### 设置失败操作
|
||||
|
||||
```powershell
|
||||
# 第一次失败:重启服务
|
||||
.\nssm.exe set RemoteApp AppExit Default Restart
|
||||
|
||||
# 重启延迟(毫秒)
|
||||
.\nssm.exe set RemoteApp AppRestartDelay 5000
|
||||
|
||||
# 设置 Windows 服务恢复选项
|
||||
.\nssm.exe set RemoteApp AppThrottle 1500
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、同时安装 FRP 和 Remote 服务
|
||||
|
||||
### 完整安装脚本
|
||||
|
||||
创建 `install-all-services.ps1`:
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行
|
||||
$nssmPath = "C:\Users\xuanchi\Desktop\remote\nssm\nssm.exe"
|
||||
$nodePath = "C:\Program Files\nodejs\node.exe"
|
||||
$appPath = "C:\Users\xuanchi\Desktop\remote"
|
||||
$frpPath = "C:\Users\xuanchi\Desktop\remote\frp"
|
||||
$logPath = "C:\Users\xuanchi\Desktop\remote\logs"
|
||||
|
||||
# 创建日志目录
|
||||
if (-not (Test-Path $logPath)) {
|
||||
New-Item -ItemType Directory -Path $logPath
|
||||
}
|
||||
|
||||
# ==================== 安装 FRP 客户端服务 ====================
|
||||
|
||||
Write-Host "Installing FRP Client service..."
|
||||
|
||||
& $nssmPath install FRPClient "$frpPath\frpc.exe" "-c frpc.toml"
|
||||
& $nssmPath set FRPClient AppDirectory $frpPath
|
||||
& $nssmPath set FRPClient DisplayName "FRP Client"
|
||||
& $nssmPath set FRPClient Description "FRP Client for Remote Access"
|
||||
& $nssmPath set FRPClient Start SERVICE_AUTO_START
|
||||
& $nssmPath set FRPClient AppStdout "$logPath\frpc-service.log"
|
||||
& $nssmPath set FRPClient AppStderr "$logPath\frpc-error.log"
|
||||
& $nssmPath set FRPClient AppRotateFiles 1
|
||||
& $nssmPath set FRPClient AppRotateBytes 1048576
|
||||
& $nssmPath set FRPClient AppExit Default Restart
|
||||
& $nssmPath set FRPClient AppRestartDelay 5000
|
||||
|
||||
# ==================== 安装 Remote 应用服务 ====================
|
||||
|
||||
Write-Host "Installing Remote App service..."
|
||||
|
||||
& $nssmPath install RemoteApp $nodePath "index.js"
|
||||
& $nssmPath set RemoteApp AppDirectory $appPath
|
||||
& $nssmPath set RemoteApp DisplayName "Remote Desktop Application"
|
||||
& $nssmPath set RemoteApp Description "Remote screen streaming service"
|
||||
& $nssmPath set RemoteApp Start SERVICE_AUTO_START
|
||||
& $nssmPath set RemoteApp AppStdout "$logPath\service.log"
|
||||
& $nssmPath set RemoteApp AppStderr "$logPath\error.log"
|
||||
& $nssmPath set RemoteApp AppRotateFiles 1
|
||||
& $nssmPath set RemoteApp AppRotateBytes 1048576
|
||||
& $nssmPath set RemoteApp AppExit Default Restart
|
||||
& $nssmPath set RemoteApp AppRestartDelay 5000
|
||||
|
||||
# 设置 RemoteApp 依赖 FRPClient
|
||||
& $nssmPath set RemoteApp DependOnService FRPClient
|
||||
|
||||
# ==================== 启动服务 ====================
|
||||
|
||||
Write-Host "Starting services..."
|
||||
|
||||
& $nssmPath start FRPClient
|
||||
Start-Sleep -Seconds 3
|
||||
& $nssmPath start RemoteApp
|
||||
|
||||
Write-Host "All services installed and started successfully!"
|
||||
Write-Host ""
|
||||
Write-Host "Services installed:"
|
||||
Write-Host " - FRPClient (FRP 客户端)"
|
||||
Write-Host " - RemoteApp (远程桌面应用)"
|
||||
Write-Host ""
|
||||
Write-Host "Access URL: http://146.56.248.142:8080"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、卸载服务
|
||||
|
||||
### 卸载脚本
|
||||
|
||||
创建 `uninstall-services.ps1`:
|
||||
|
||||
```powershell
|
||||
# 以管理员身份运行
|
||||
$nssmPath = "C:\Users\xuanchi\Desktop\remote\nssm\nssm.exe"
|
||||
|
||||
# 停止并删除 RemoteApp
|
||||
Write-Host "Removing RemoteApp service..."
|
||||
& $nssmPath stop RemoteApp
|
||||
& $nssmPath remove RemoteApp confirm
|
||||
|
||||
# 停止并删除 FRPClient
|
||||
Write-Host "Removing FRPClient service..."
|
||||
& $nssmPath stop FRPClient
|
||||
& $nssmPath remove FRPClient confirm
|
||||
|
||||
Write-Host "All services removed successfully!"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 八、常用命令速查
|
||||
|
||||
| 操作 | 命令 |
|
||||
|------|------|
|
||||
| 安装服务 | `nssm install <服务名> <程序> [参数]` |
|
||||
| 启动服务 | `nssm start <服务名>` |
|
||||
| 停止服务 | `nssm stop <服务名>` |
|
||||
| 重启服务 | `nssm restart <服务名>` |
|
||||
| 查看状态 | `nssm status <服务名>` |
|
||||
| 编辑配置 | `nssm edit <服务名>` |
|
||||
| 删除服务 | `nssm remove <服务名>` |
|
||||
| 设置参数 | `nssm set <服务名> <参数名> <值>` |
|
||||
| 获取参数 | `nssm get <服务名> <参数名>` |
|
||||
|
||||
---
|
||||
|
||||
## 九、常见问题
|
||||
|
||||
### 1. 服务启动后立即停止
|
||||
|
||||
**原因:** 程序路径错误或依赖未满足
|
||||
|
||||
**解决:**
|
||||
```powershell
|
||||
# 检查服务日志
|
||||
type C:\Users\xuanchi\Desktop\remote\logs\error.log
|
||||
|
||||
# 检查程序路径
|
||||
nssm get RemoteApp Application
|
||||
```
|
||||
|
||||
### 2. 权限不足
|
||||
|
||||
**解决:** 以管理员身份运行 PowerShell
|
||||
|
||||
### 3. 端口被占用
|
||||
|
||||
```powershell
|
||||
# 查看端口占用
|
||||
netstat -ano | findstr :3000
|
||||
|
||||
# 结束进程
|
||||
taskkill /PID <PID> /F
|
||||
```
|
||||
|
||||
### 4. 服务无法访问网络
|
||||
|
||||
**解决:**
|
||||
```powershell
|
||||
# 设置服务依赖网络
|
||||
nssm set RemoteApp DependOnService Tcpip Dhcp Dnscache
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、参考链接
|
||||
|
||||
- NSSM 官网: https://nssm.cc/
|
||||
- NSSM 下载: https://nssm.cc/download
|
||||
- NSSM 文档: https://nssm.cc/usage
|
||||
382
remote/docs/指南/OpenFRP本地客户端配置指南.md
Normal file
382
remote/docs/指南/OpenFRP本地客户端配置指南.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# OpenFRP 本地客户端配置指南
|
||||
|
||||
> 本指南面向本地 Remote 项目,介绍如何配置和管理 OpenFRP 客户端,实现内网穿透。
|
||||
|
||||
---
|
||||
|
||||
## 一、OpenFRP 简介
|
||||
|
||||
OpenFRP 是一个高性能的反向代理应用,用于将内网服务暴露到公网。
|
||||
|
||||
### 工作原理
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────────────┐ ┌─────────────┐
|
||||
│ 外部用户 │ ──────▶ │ FRP Server (公网) │ ◀────── │ FRP Client │
|
||||
│ │ │ 146.56.248.142 │ 隧道 │ (本机) │
|
||||
└─────────────┘ │ :8080 → 转发 │ │ :3000 │
|
||||
└─────────────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
**工作流程:**
|
||||
1. frpc 启动后主动连接 frps,建立持久隧道
|
||||
2. 外部用户访问 `146.56.248.142:8080`
|
||||
3. frps 将请求通过隧道转发给本地 frpc
|
||||
4. frpc 将请求转发到本地 `127.0.0.1:3000`
|
||||
5. 响应按原路返回
|
||||
|
||||
---
|
||||
|
||||
## 二、本地项目配置
|
||||
|
||||
### 当前配置文件
|
||||
|
||||
配置文件位置:`frp/frpc.toml`
|
||||
|
||||
```toml
|
||||
serverAddr = "146.56.248.142"
|
||||
serverPort = 7000
|
||||
auth.token = "wzw20040525"
|
||||
|
||||
log.to = "C:\\frp\\frpc.log"
|
||||
log.level = "info"
|
||||
log.maxDays = 7
|
||||
|
||||
[[proxies]]
|
||||
name = "remote-desktop"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3000
|
||||
remotePort = 8080
|
||||
```
|
||||
|
||||
### 配置项说明
|
||||
|
||||
| 参数 | 说明 | 当前值 |
|
||||
|------|------|--------|
|
||||
| serverAddr | FRP 服务端地址 | 146.56.248.142 |
|
||||
| serverPort | FRP 服务端端口 | 7000 |
|
||||
| auth.token | 认证令牌 | wzw20040525 |
|
||||
| log.to | 日志文件路径 | C:\frp\frpc.log |
|
||||
| log.level | 日志级别 | info |
|
||||
| log.maxDays | 日志保留天数 | 7 |
|
||||
| [[proxies]] | 代理配置块 | - |
|
||||
| name | 代理名称 | remote-desktop |
|
||||
| type | 代理类型 | tcp |
|
||||
| localIP | 本地服务 IP | 127.0.0.1 |
|
||||
| localPort | 本地服务端口 | 3000 |
|
||||
| remotePort | 远程访问端口 | 8080 |
|
||||
|
||||
---
|
||||
|
||||
## 三、启动与管理
|
||||
|
||||
### 启动 FRP 客户端
|
||||
|
||||
```powershell
|
||||
# 进入 frp 目录
|
||||
cd C:\Users\xuanchi\Desktop\remote\frp
|
||||
|
||||
# 启动客户端
|
||||
.\frpc.exe -c frpc.toml
|
||||
```
|
||||
|
||||
**成功输出:**
|
||||
```
|
||||
[start] frpc started successfully
|
||||
```
|
||||
|
||||
### 启动顺序
|
||||
|
||||
**必须先启动 FRP 客户端,再启动 Remote 应用**
|
||||
|
||||
1. 启动 FRP 客户端
|
||||
2. 启动 Remote 应用:`node index.js`
|
||||
|
||||
### 访问地址
|
||||
|
||||
| 访问方式 | 地址 |
|
||||
|---------|------|
|
||||
| 本地访问 | http://localhost:3000 |
|
||||
| 外网访问 | http://146.56.248.142:8080 |
|
||||
|
||||
---
|
||||
|
||||
## 四、多代理配置
|
||||
|
||||
可以在同一个配置文件中添加多个代理映射:
|
||||
|
||||
```toml
|
||||
serverAddr = "146.56.248.142"
|
||||
serverPort = 7000
|
||||
auth.token = "wzw20040525"
|
||||
|
||||
log.to = "C:\\frp\\frpc.log"
|
||||
log.level = "info"
|
||||
log.maxDays = 7
|
||||
|
||||
# 远程桌面应用
|
||||
[[proxies]]
|
||||
name = "remote-desktop"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3000
|
||||
remotePort = 8080
|
||||
|
||||
# SSH 服务
|
||||
[[proxies]]
|
||||
name = "ssh"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
remotePort = 2222
|
||||
|
||||
# Web 服务
|
||||
[[proxies]]
|
||||
name = "web"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 80
|
||||
remotePort = 8000
|
||||
|
||||
# MySQL 数据库
|
||||
[[proxies]]
|
||||
name = "mysql"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3306
|
||||
remotePort = 3306
|
||||
```
|
||||
|
||||
**注意事项:**
|
||||
- 每个代理的 `name` 必须唯一
|
||||
- `remotePort` 不能重复
|
||||
- 添加新代理后需要重启 frpc
|
||||
|
||||
---
|
||||
|
||||
## 五、代理类型
|
||||
|
||||
### TCP 代理
|
||||
|
||||
最常用的类型,适用于大多数服务。
|
||||
|
||||
```toml
|
||||
[[proxies]]
|
||||
name = "tcp-service"
|
||||
type = "tcp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3000
|
||||
remotePort = 8080
|
||||
```
|
||||
|
||||
### UDP 代理
|
||||
|
||||
适用于 DNS、游戏服务器等。
|
||||
|
||||
```toml
|
||||
[[proxies]]
|
||||
name = "udp-service"
|
||||
type = "udp"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 53
|
||||
remotePort = 5353
|
||||
```
|
||||
|
||||
### HTTP 代理
|
||||
|
||||
适用于 Web 服务,支持域名路由。
|
||||
|
||||
```toml
|
||||
[[proxies]]
|
||||
name = "http-service"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 80
|
||||
customDomains = ["www.example.com"]
|
||||
```
|
||||
|
||||
### STCP (Secret TCP)
|
||||
|
||||
需要访问密钥,更安全。
|
||||
|
||||
```toml
|
||||
# 服务端配置
|
||||
[[proxies]]
|
||||
name = "secret-service"
|
||||
type = "stcp"
|
||||
secretKey = "your-secret-key"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 22
|
||||
|
||||
# 访问端配置 (另一个 frpc)
|
||||
[[visitors]]
|
||||
name = "secret-visitor"
|
||||
type = "stcp"
|
||||
serverName = "secret-service"
|
||||
secretKey = "your-secret-key"
|
||||
bindAddr = "127.0.0.1"
|
||||
bindPort = 2222
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、Windows 服务配置
|
||||
|
||||
### 使用 NSSM 安装为服务
|
||||
|
||||
1. 下载 NSSM: https://nssm.cc/download
|
||||
|
||||
2. 安装服务:
|
||||
```powershell
|
||||
nssm install FRPClient "C:\Users\xuanchi\Desktop\remote\frp\frpc.exe" "-c" "C:\Users\xuanchi\Desktop\remote\frp\frpc.toml"
|
||||
nssm start FRPClient
|
||||
```
|
||||
|
||||
3. 管理服务:
|
||||
```powershell
|
||||
nssm stop FRPClient
|
||||
nssm restart FRPClient
|
||||
nssm remove FRPClient
|
||||
```
|
||||
|
||||
### 使用 sc 命令
|
||||
|
||||
```powershell
|
||||
sc create FRPClient binPath= "C:\Users\xuanchi\Desktop\remote\frp\frpc.exe -c C:\Users\xuanchi\Desktop\remote\frp\frpc.toml" start= auto
|
||||
sc start FRPClient
|
||||
sc stop FRPClient
|
||||
sc delete FRPClient
|
||||
```
|
||||
|
||||
### 使用任务计划程序
|
||||
|
||||
1. 搜索"任务计划程序"
|
||||
2. 创建基本任务
|
||||
3. 触发器:开机时
|
||||
4. 操作:启动程序
|
||||
5. 程序:`C:\Users\xuanchi\Desktop\remote\frp\frpc.exe`
|
||||
6. 参数:`-c frpc.toml`
|
||||
7. 起始位置:`C:\Users\xuanchi\Desktop\remote\frp`
|
||||
|
||||
---
|
||||
|
||||
## 七、常见问题排查
|
||||
|
||||
### 1. 连接失败
|
||||
|
||||
**错误信息:**
|
||||
```
|
||||
login to server failed: dial tcp xxx.xxx.xxx.xxx:7000: connectex: A connection attempt failed
|
||||
```
|
||||
|
||||
**排查步骤:**
|
||||
1. 检查服务端是否运行
|
||||
2. 检查防火墙是否开放端口
|
||||
3. 检查 auth.token 是否一致
|
||||
4. 测试端口连通性:
|
||||
```powershell
|
||||
Test-NetConnection -ComputerName 146.56.248.142 -Port 7000
|
||||
```
|
||||
|
||||
### 2. 端口被占用
|
||||
|
||||
```powershell
|
||||
# 查看端口占用
|
||||
netstat -ano | findstr :8080
|
||||
|
||||
# 结束进程
|
||||
taskkill /PID <PID> /F
|
||||
```
|
||||
|
||||
### 3. 查看日志
|
||||
|
||||
```powershell
|
||||
# 查看日志文件
|
||||
type C:\frp\frpc.log
|
||||
|
||||
# 实时查看日志
|
||||
Get-Content C:\frp\frpc.log -Wait
|
||||
```
|
||||
|
||||
### 4. 浏览器显示 ERR_UNSAFE_PORT
|
||||
|
||||
**原因:** Chrome/Edge 阻止了某些端口(如 6000、4045 等)
|
||||
|
||||
**解决方法:** 修改 `remotePort` 为安全端口(8080、8000、5000、8888、3001)
|
||||
|
||||
### 5. 连接成功但无画面
|
||||
|
||||
**排查步骤:**
|
||||
1. 检查本地访问 `http://localhost:3000` 是否正常
|
||||
2. 检查 Remote 应用是否启动
|
||||
3. 检查 FRP 日志是否有错误
|
||||
|
||||
---
|
||||
|
||||
## 八、常用命令速查
|
||||
|
||||
```powershell
|
||||
# 启动 FRP 客户端
|
||||
.\frpc.exe -c frpc.toml
|
||||
|
||||
# 查看进程
|
||||
tasklist | findstr frpc
|
||||
|
||||
# 停止 FRP
|
||||
taskkill /F /IM frpc.exe
|
||||
|
||||
# 重启 FRP
|
||||
taskkill /F /IM frpc.exe; .\frpc.exe -c frpc.toml
|
||||
|
||||
# 测试端口连通性
|
||||
Test-NetConnection -ComputerName 146.56.248.142 -Port 7000
|
||||
|
||||
# 查看日志
|
||||
type C:\frp\frpc.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 九、安全建议
|
||||
|
||||
1. **使用强密码令牌**
|
||||
```toml
|
||||
auth.token = "complex_random_string_here"
|
||||
```
|
||||
|
||||
2. **使用 STCP 替代 TCP**
|
||||
- 需要密钥才能访问,更安全
|
||||
|
||||
3. **启用 TLS 加密**
|
||||
```toml
|
||||
transport.tls.enable = true
|
||||
```
|
||||
|
||||
4. **定期更换 Token**
|
||||
- 建议每 3-6 个月更换一次
|
||||
|
||||
---
|
||||
|
||||
## 十、常用端口参考
|
||||
|
||||
| 服务 | 默认端口 | 说明 |
|
||||
|------|---------|------|
|
||||
| SSH | 22 | 远程登录 |
|
||||
| HTTP | 80 | Web 服务 |
|
||||
| HTTPS | 443 | 安全 Web 服务 |
|
||||
| 远程桌面 (RDP) | 3389 | Windows 远程桌面 |
|
||||
| MySQL | 3306 | 数据库 |
|
||||
| Redis | 6379 | 缓存服务 |
|
||||
| PostgreSQL | 5432 | 数据库 |
|
||||
| MongoDB | 27017 | 数据库 |
|
||||
| VNC | 5900 | 远程桌面 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、参考链接
|
||||
|
||||
- FRP 官方文档: https://gofrp.org/zh-cn/docs/
|
||||
- FRP GitHub: https://github.com/fatedier/frp
|
||||
- FRP 下载地址: https://github.com/fatedier/frp/releases
|
||||
- 腾讯云 OpenFRP 配置教程: [腾讯云 OpenFRP 配置教程.md](./腾讯云%20OpenFRP%20配置教程.md)
|
||||
320
remote/docs/指南/腾讯云 BBR 优化指南.md
Normal file
320
remote/docs/指南/腾讯云 BBR 优化指南.md
Normal file
@@ -0,0 +1,320 @@
|
||||
# 腾讯云服务器 BBR 优化指南
|
||||
|
||||
> 本指南详细讲解如何在腾讯云轻量服务器上开启 BBR 拥塞控制算法,优化 FRP 内网穿透性能。
|
||||
>
|
||||
> **适用场景**:远程桌面、视频流传输、高延迟网络环境
|
||||
>
|
||||
> **预计耗时**:5 分钟
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [一、BBR 简介](#一bbr-简介)
|
||||
- [二、为什么远程桌面需要 BBR](#二为什么远程桌面需要-bbr)
|
||||
- [三、开启 BBR](#三开启-bbr)
|
||||
- [四、验证配置](#四验证配置)
|
||||
- [五、效果分析](#五效果分析)
|
||||
- [六、常见问题](#六常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 一、BBR 简介
|
||||
|
||||
### 1.1 什么是 BBR?
|
||||
|
||||
BBR(Bottleneck Bandwidth and RTT)是 Google 开发的 TCP 拥塞控制算法,通过实时测量网络带宽和往返延迟来优化数据传输。
|
||||
|
||||
### 1.2 BBR vs 传统算法
|
||||
|
||||
| 对比项 | 传统算法(Cubic/Reno) | BBR |
|
||||
|--------|----------------------|-----|
|
||||
| 判断依据 | 丢包 = 网络拥堵 | 实时测量带宽和延迟 |
|
||||
| 遇到丢包 | 大幅降速 | 保持稳定 |
|
||||
| 高延迟网络 | 性能下降明显 | 保持稳定 |
|
||||
| 网络波动 | 频繁调整 | 平滑过渡 |
|
||||
|
||||
### 1.3 BBR 的核心作用
|
||||
|
||||
**一句话总结**:BBR 让 TCP 连接在"烂网络"下表现更好,而不是让"好网络"变得更快。
|
||||
|
||||
**BBR 不能**:
|
||||
- ❌ 突破物理带宽上限
|
||||
- ❌ 让好网络变得更快
|
||||
- ❌ 增加运营商给的上行/下行速度
|
||||
|
||||
**BBR 能**:
|
||||
- ✅ 高延迟网络下保持稳定传输
|
||||
- ✅ 有丢包环境下减少性能损失
|
||||
- ✅ 网络波动时平滑过渡
|
||||
|
||||
---
|
||||
|
||||
## 二、为什么远程桌面需要 BBR
|
||||
|
||||
### 2.1 远程桌面的网络特性
|
||||
|
||||
远程桌面应用具有以下特点:
|
||||
- 持续的视频流传输
|
||||
- 对延迟敏感
|
||||
- 对丢包敏感(视频卡顿)
|
||||
- 需要稳定的带宽
|
||||
|
||||
### 2.2 FRP 内网穿透架构
|
||||
|
||||
```
|
||||
本地电脑 → FRP客户端 → 腾讯云服务器 → 外网用户
|
||||
TCP长连接 TCP转发
|
||||
```
|
||||
|
||||
BBR 在服务端开启后:
|
||||
- 优化 FRP 服务端 → 外网用户的 TCP 连接
|
||||
- 优化 FRP 客户端 → 服务端的 TCP 连接(双向收益)
|
||||
|
||||
### 2.3 适用场景分析
|
||||
|
||||
| 场景 | BBR 效果 |
|
||||
|------|----------|
|
||||
| 本地网络质量好(低延迟、无丢包) | 几乎无差异 |
|
||||
| 跨国/跨省访问(高延迟) | 明显提升 |
|
||||
| WiFi 信号不稳定(有丢包) | 明显更流畅 |
|
||||
| 高峰期网络拥堵 | 更稳定 |
|
||||
| 移动网络访问(4G/5G) | 更稳定 |
|
||||
|
||||
---
|
||||
|
||||
## 三、开启 BBR
|
||||
|
||||
### 3.1 检查当前配置
|
||||
|
||||
SSH 连接到腾讯云服务器:
|
||||
|
||||
```bash
|
||||
# 检查当前拥塞控制算法
|
||||
sysctl net.ipv4.tcp_congestion_control
|
||||
# 默认输出:net.ipv4.tcp_congestion_control = cubic
|
||||
|
||||
# 检查内核版本(Ubuntu 24.04 默认支持 BBR)
|
||||
uname -r
|
||||
# 输出:6.8.x-generic
|
||||
```
|
||||
|
||||
### 3.2 开启 BBR
|
||||
|
||||
```bash
|
||||
# 添加 BBR 配置
|
||||
echo "net.core.default_qdisc=fq" | sudo tee -a /etc/sysctl.conf
|
||||
echo "net.ipv4.tcp_congestion_control=bbr" | sudo tee -a /etc/sysctl.conf
|
||||
|
||||
# 应用配置
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
### 3.3 添加网络优化参数(推荐)
|
||||
|
||||
编辑配置文件:
|
||||
|
||||
```bash
|
||||
sudo nano /etc/sysctl.conf
|
||||
```
|
||||
|
||||
添加以下内容:
|
||||
|
||||
```ini
|
||||
# BBR 基础配置
|
||||
net.core.default_qdisc = fq
|
||||
net.ipv4.tcp_congestion_control = bbr
|
||||
|
||||
# BBR 配合的网络优化
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.wmem_max = 16777216
|
||||
net.ipv4.tcp_rmem = 4096 87380 16777216
|
||||
net.ipv4.tcp_wmem = 4096 87380 16777216
|
||||
net.core.netdev_max_backlog = 5000
|
||||
net.ipv4.tcp_fastopen = 3
|
||||
net.ipv4.tcp_slow_start_after_idle = 0
|
||||
```
|
||||
|
||||
应用配置:
|
||||
|
||||
```bash
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
### 3.4 重启 FRP 服务
|
||||
|
||||
让新的网络配置对 FRP 连接生效:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart frps
|
||||
sudo systemctl status frps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、验证配置
|
||||
|
||||
### 4.1 验证 BBR 是否生效
|
||||
|
||||
```bash
|
||||
# 检查拥塞控制算法
|
||||
sysctl net.ipv4.tcp_congestion_control
|
||||
# 输出:net.ipv4.tcp_congestion_control = bbr
|
||||
|
||||
# 检查内核模块
|
||||
lsmod | grep bbr
|
||||
# 输出:tcp_bbr 20480 X
|
||||
```
|
||||
|
||||
### 4.2 验证 TCP 连接使用 BBR
|
||||
|
||||
```bash
|
||||
# 查看 FRP 的 TCP 连接是否使用 BBR
|
||||
ss -tin | grep -A 1 :7000
|
||||
```
|
||||
|
||||
期望输出(看到 `bbr` 字样):
|
||||
|
||||
```
|
||||
ESTAB 0 0 146.56.248.142:7000 xxx.xxx.xxx.xxx:xxxx
|
||||
bbr cwnd:10 ...
|
||||
```
|
||||
|
||||
### 4.3 配置验证清单
|
||||
|
||||
| 检查项 | 命令 | 期望结果 |
|
||||
|--------|------|----------|
|
||||
| BBR 算法生效 | `sysctl net.ipv4.tcp_congestion_control` | `= bbr` |
|
||||
| 队列调度器 | `sysctl net.core.default_qdisc` | `= fq` |
|
||||
| 内核模块加载 | `lsmod \| grep bbr` | `tcp_bbr ...` |
|
||||
| FRP 服务运行 | `sudo systemctl status frps` | `active (running)` |
|
||||
|
||||
---
|
||||
|
||||
## 五、效果分析
|
||||
|
||||
### 5.1 为什么带宽没有变化?
|
||||
|
||||
这是**正常现象**,原因如下:
|
||||
|
||||
**1. BBR 不增加带宽上限**
|
||||
|
||||
BBR 是优化 TCP 拥塞控制的算法,它不能突破物理带宽限制:
|
||||
|
||||
| 误解 | 实际 |
|
||||
|------|------|
|
||||
| BBR 会增加带宽 | ❌ 带宽上限由运营商决定 |
|
||||
| BBR 会提升速度 | ❌ 只是在弱网环境下更稳定 |
|
||||
|
||||
**2. 瓶颈可能在本地**
|
||||
|
||||
```
|
||||
完整链路:
|
||||
本地电脑(上行) → FRP客户端 → 腾讯云服务器(200Mbps) → 外网用户
|
||||
↑
|
||||
瓶颈在这里
|
||||
```
|
||||
|
||||
家庭宽带典型上行:
|
||||
- 100M 宽带:上行约 20-30 Mbps
|
||||
- 200M 宽带:上行约 30-50 Mbps
|
||||
- 有些地区上行只有 10 Mbps
|
||||
|
||||
### 5.2 BBR 真正有用的场景
|
||||
|
||||
| 场景 | 传统算法 | BBR | 效果 |
|
||||
|------|----------|-----|------|
|
||||
| 网络好(低延迟、无丢包) | 满速 | 满速 | 无差异 |
|
||||
| 高延迟(跨省/跨国) | 下降 30-50% | 保持稳定 | 明显提升 |
|
||||
| 有丢包(WiFi/移动网络) | 大幅下降 | 基本不受影响 | 明显提升 |
|
||||
| 网络波动 | 频繁卡顿 | 平滑过渡 | 更稳定 |
|
||||
|
||||
### 5.3 如何正确测试 BBR 效果
|
||||
|
||||
**方法 1:实际场景测试**
|
||||
|
||||
- 找一个**远距离**的朋友访问(跨省)
|
||||
- 在**手机 4G/5G 网络**下访问
|
||||
- 在**网络高峰期**(晚上 8-10 点)测试
|
||||
|
||||
**方法 2:使用 iperf3 测试**
|
||||
|
||||
服务器端:
|
||||
|
||||
```bash
|
||||
sudo apt install iperf3 -y
|
||||
iperf3 -s
|
||||
```
|
||||
|
||||
客户端:
|
||||
|
||||
```powershell
|
||||
iperf3 -c 146.56.248.142 -t 30
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、常见问题
|
||||
|
||||
### 6.1 BBR 开启后带宽没变化?
|
||||
|
||||
这是正常的,BBR 不增加带宽上限。BBR 的作用是在弱网环境下保持稳定,而不是提升带宽。
|
||||
|
||||
### 6.2 如何回滚 BBR?
|
||||
|
||||
如果需要恢复到默认算法:
|
||||
|
||||
```bash
|
||||
sudo sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf
|
||||
sudo sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf
|
||||
echo "net.ipv4.tcp_congestion_control=cubic" | sudo tee -a /etc/sysctl.conf
|
||||
echo "net.core.default_qdisc=fq_codel" | sudo tee -a /etc/sysctl.conf
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
### 6.3 BBR v1 和 BBR v2 的区别?
|
||||
|
||||
Ubuntu 24.04 默认是 BBR v1,已经足够优秀。BBR v2/v3 需要手动编译内核模块,不推荐新手操作。
|
||||
|
||||
### 6.4 腾讯云是否支持 BBR?
|
||||
|
||||
腾讯云轻量服务器默认没有限制 BBR,可以直接开启。
|
||||
|
||||
### 6.5 需要在本地电脑也开启 BBR 吗?
|
||||
|
||||
不需要。服务端开启 BBR 已经可以优化 TCP 连接的双向性能。
|
||||
|
||||
---
|
||||
|
||||
## 附录:配置参数说明
|
||||
|
||||
| 参数 | 说明 | 推荐值 |
|
||||
|------|------|--------|
|
||||
| `net.core.default_qdisc` | 默认队列调度算法 | fq |
|
||||
| `net.ipv4.tcp_congestion_control` | TCP 拥塞控制算法 | bbr |
|
||||
| `net.core.rmem_max` | TCP 读缓冲区最大值 | 16777216 (16MB) |
|
||||
| `net.core.wmem_max` | TCP 写缓冲区最大值 | 16777216 (16MB) |
|
||||
| `net.ipv4.tcp_rmem` | TCP 读缓冲区动态调整 | 4096 87380 16777216 |
|
||||
| `net.ipv4.tcp_wmem` | TCP 写缓冲区动态调整 | 4096 87380 16777216 |
|
||||
| `net.core.netdev_max_backlog` | 网卡队列长度 | 5000 |
|
||||
| `net.ipv4.tcp_fastopen` | TCP Fast Open | 3 (客户端+服务端) |
|
||||
| `net.ipv4.tcp_slow_start_after_idle` | 空闲后慢启动 | 0 (禁用) |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 问题 | 答案 |
|
||||
|------|------|
|
||||
| BBR 能提升带宽上限吗? | ❌ 不能 |
|
||||
| BBR 能让好网络更快吗? | ❌ 几乎不能 |
|
||||
| BBR 能让弱网更稳定吗? | ✅ 能,效果明显 |
|
||||
| 远程桌面场景需要 BBR 吗? | ✅ 需要(外网访问场景多) |
|
||||
|
||||
**建议**:保持 BBR 开启,没有坏处,在跨省访问、移动网络、网络高峰期时会有更好的体验。
|
||||
|
||||
---
|
||||
|
||||
*文档版本:v1.0*
|
||||
*更新时间:2026-03-03*
|
||||
*适用系统:Ubuntu 24.04 LTS*
|
||||
977
remote/docs/指南/腾讯云 OpenFRP 配置教程.md
Normal file
977
remote/docs/指南/腾讯云 OpenFRP 配置教程.md
Normal file
@@ -0,0 +1,977 @@
|
||||
# 腾讯云 + OpenFRP 内网穿透完整配置教程
|
||||
|
||||
> 本教程详细讲解如何使用腾讯云轻量服务器 + OpenFRP 实现内网穿透,让你的远程桌面应用可以从外网访问。
|
||||
>
|
||||
> **适用场景**:远程办公、远程桌面、内网服务暴露等
|
||||
>
|
||||
> **预计耗时**:30-60 分钟
|
||||
>
|
||||
> **成本**:约 45 元/月(腾讯云服务器费用)
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [一、准备工作](#一准备工作)
|
||||
- [二、购买并配置腾讯云服务器](#二购买并配置腾讯云服务器)
|
||||
- [三、配置 FRP 服务端](#三配置 frp 服务端)
|
||||
- [四、配置本地 FRP 客户端](#四配置本地 frp 客户端)
|
||||
- [五、启动和测试](#五启动和测试)
|
||||
- [六、常见问题排查](#六常见问题排查)
|
||||
- [七、优化建议](#七优化建议)
|
||||
|
||||
---
|
||||
|
||||
## 一、准备工作
|
||||
|
||||
### 1.1 所需材料
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 腾讯云账号 | 需要实名认证 |
|
||||
| 本地电脑 | Windows 系统 |
|
||||
| Remote 应用 | 已配置好的远程桌面应用 |
|
||||
| 预算 | 约 45 元/月 |
|
||||
|
||||
### 1.2 技术栈说明
|
||||
|
||||
| 组件 | 版本 | 作用 |
|
||||
|------|------|------|
|
||||
| 腾讯云轻量服务器 | 2 核 2GB 200Mbps | FRP 服务端 + 流量中转 |
|
||||
| Ubuntu | 24.04 LTS | 服务器操作系统 |
|
||||
| FRP | v0.61.1 | 内网穿透工具 |
|
||||
| Node.js | v16+ | 运行 Remote 应用 |
|
||||
|
||||
### 1.3 端口规划
|
||||
|
||||
| 端口 | 用途 | 说明 |
|
||||
|------|------|------|
|
||||
| 7000 | FRP 服务端监听端口 | 客户端连接服务端 |
|
||||
| 7500 | FRP 管理面板 | 查看连接状态 |
|
||||
| 8080 | 远程桌面访问端口 | 浏览器访问此端口 |
|
||||
| 3000 | Remote 应用本地端口 | 本地服务运行端口 |
|
||||
|
||||
---
|
||||
|
||||
## 二、购买并配置腾讯云服务器
|
||||
|
||||
### 2.1 购买服务器
|
||||
|
||||
#### 步骤 1:进入腾讯云官网
|
||||
|
||||
1. 访问 https://cloud.tencent.com/
|
||||
2. 登录账号(需要完成实名认证)
|
||||
|
||||
#### 步骤 2:选择轻量应用服务器
|
||||
|
||||
1. 导航栏选择 **产品** → **云服务器** → **轻量应用服务器**
|
||||
2. 点击 **立即购买**
|
||||
|
||||
#### 步骤 3:选择配置
|
||||
|
||||
**套餐选择**:
|
||||
```
|
||||
锐驰型套餐
|
||||
- 地域:上海/北京/广州/成都/南京(选择离你最近的)
|
||||
- CPU&内存:2 核 2GB
|
||||
- 系统盘:40GB SSD
|
||||
- 峰值带宽:200Mbps
|
||||
- 流量包:无限流量
|
||||
- 价格:45 元/月 或 459 元/年(85 折)
|
||||
```
|
||||
|
||||
**镜像选择**:
|
||||
```
|
||||
操作系统:Ubuntu 24.04 LTS 64 位 ✅
|
||||
```
|
||||
|
||||
> ⚠️ **重要**:不要选择 Windows Server,需要额外授权费且资源占用高
|
||||
|
||||
#### 步骤 4:配置登录方式
|
||||
|
||||
**推荐选择**:自定义密码
|
||||
```
|
||||
用户名:ubuntu
|
||||
密码:设置一个强密码(建议 12 位以上,包含大小写字母 + 数字 + 特殊符号)
|
||||
```
|
||||
|
||||
#### 步骤 5:确认购买
|
||||
|
||||
1. 选择购买时长(建议直接买 1 年,享受 85 折)
|
||||
2. 勾选服务协议
|
||||
3. 点击"立即购买"并完成支付
|
||||
|
||||
---
|
||||
|
||||
### 2.2 获取服务器信息
|
||||
|
||||
购买完成后,在腾讯云控制台记录以下信息:
|
||||
|
||||
```
|
||||
公网 IP 地址:xxx.xxx.xxx.xxx
|
||||
用户名:ubuntu
|
||||
密码:你设置的密码
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.3 SSH 连接服务器
|
||||
|
||||
#### Windows 用户(PowerShell)
|
||||
|
||||
```powershell
|
||||
# 打开 PowerShell
|
||||
ssh ubuntu@你的服务器公网 IP
|
||||
|
||||
# 首次连接会提示确认指纹,输入 yes
|
||||
# 然后输入密码(输入时不显示,正常输入即可)
|
||||
```
|
||||
|
||||
#### 成功登录后看到:
|
||||
|
||||
```
|
||||
Welcome to Ubuntu 24.04 LTS (GNU/Linux ...)
|
||||
ubuntu@VM-4-13-ubuntu:~$
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 配置服务器防火墙(安全组)
|
||||
|
||||
#### 步骤 1:进入安全组配置
|
||||
|
||||
1. 登录腾讯云控制台
|
||||
2. 进入 **轻量应用服务器** 页面
|
||||
3. 找到你的服务器实例,点击进入详情页
|
||||
4. 选择 **防火墙** 或 **安全组** 标签页
|
||||
5. 点击 **添加规则** 或 **配置规则**
|
||||
|
||||
#### 步骤 2:添加入站规则
|
||||
|
||||
添加以下 3 条规则:
|
||||
|
||||
| 应用类型 | 来源 | 协议 | 端口 | 策略 | 备注 |
|
||||
|---------|------|------|------|------|------|
|
||||
| 自定义 | 全部 IPv4 地址 | TCP | 7000 | 允许 | FRP 客户端连接 |
|
||||
| 自定义 | 全部 IPv4 地址 | TCP | 7500 | 允许 | FRP 管理面板 |
|
||||
| 自定义 | 全部 IPv4 地址 | TCP | 8080 | 允许 | Remote 桌面访问 |
|
||||
|
||||
**添加步骤**:
|
||||
1. 点击"添加规则"
|
||||
2. 应用类型选择"自定义"
|
||||
3. 来源填写"全部 IPv4 地址"或"0.0.0.0/0"
|
||||
4. 协议选择"TCP"
|
||||
5. 端口填写"7000"
|
||||
6. 策略选择"允许"
|
||||
7. 备注填写"FRP 客户端连接"
|
||||
8. 点击确定
|
||||
|
||||
**重复以上步骤添加 7500 和 8080 端口**
|
||||
|
||||
---
|
||||
|
||||
## 三、配置 FRP 服务端
|
||||
|
||||
### 3.1 下载 FRP
|
||||
|
||||
```bash
|
||||
# SSH 连接到服务器后,执行以下命令
|
||||
|
||||
# 1. 切换到 root 用户(获取管理员权限)
|
||||
sudo -i
|
||||
|
||||
# 2. 创建 FRP 目录
|
||||
mkdir -p /opt/frp
|
||||
cd /opt/frp
|
||||
|
||||
# 3. 下载 FRP(v0.61.1 版本)
|
||||
wget https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_linux_amd64.tar.gz
|
||||
|
||||
# 4. 解压
|
||||
tar -xzvf frp_0.61.1_linux_amd64.tar.gz
|
||||
|
||||
# 5. 进入目录
|
||||
cd frp_0.61.1_linux_amd64
|
||||
|
||||
# 6. 查看文件
|
||||
ls -la
|
||||
```
|
||||
|
||||
**应该看到**:
|
||||
```
|
||||
frpc frpc.toml
|
||||
frps frps.toml
|
||||
LICENSE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 生成安全 Token
|
||||
|
||||
Token 是服务端和客户端之间的认证密码,必须保密。
|
||||
|
||||
```bash
|
||||
# 生成随机 token
|
||||
openssl rand -base64 32
|
||||
```
|
||||
|
||||
**示例输出**(保存这个值):
|
||||
```
|
||||
Kj8Hn2bVx9Qm5Lp3Wr7Yt4Za6Uc1Fd0Sg8Ik2Ow5Ev9Mx3Rq7Nj6Hb4Lp0Za2Uc
|
||||
```
|
||||
|
||||
> ⚠️ **重要**:将生成的 token 记录下来,后面配置要用!
|
||||
|
||||
---
|
||||
|
||||
### 3.3 配置 FRP 服务端
|
||||
|
||||
```bash
|
||||
# 编辑配置文件
|
||||
nano frps.ini
|
||||
```
|
||||
|
||||
**粘贴以下配置**(修改 token 为你生成的值):
|
||||
|
||||
```ini
|
||||
[common]
|
||||
bind_port = 7000
|
||||
bind_addr = 0.0.0.0
|
||||
token = 你的 token(上面 openssl 生成的值)
|
||||
dashboard_port = 7500
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = Admin@2024Secure
|
||||
log_file = /opt/frp/frps.log
|
||||
log_level = info
|
||||
log_max_days = 7
|
||||
```
|
||||
|
||||
**配置说明**:
|
||||
|
||||
| 参数 | 说明 | 推荐值 |
|
||||
|------|------|--------|
|
||||
| bind_port | FRP 服务端监听端口 | 7000 |
|
||||
| bind_addr | 监听地址(0.0.0.0 表示所有网卡) | 0.0.0.0 |
|
||||
| token | 认证密码(服务端客户端必须一致) | 随机生成的字符串 |
|
||||
| dashboard_port | 管理面板端口 | 7500 |
|
||||
| dashboard_user | 管理面板用户名 | admin |
|
||||
| dashboard_pwd | 管理面板密码 | 复杂密码 |
|
||||
| log_file | 日志文件路径 | /opt/frp/frps.log |
|
||||
| log_level | 日志级别 | info |
|
||||
| log_max_days | 日志保留天数 | 7 |
|
||||
|
||||
**保存退出**:
|
||||
1. 按 `Ctrl + O`
|
||||
2. 按 `Enter` 确认
|
||||
3. 按 `Ctrl + X` 退出
|
||||
|
||||
---
|
||||
|
||||
### 3.4 启动 FRP 服务端
|
||||
|
||||
```bash
|
||||
# 后台启动 FRP
|
||||
nohup ./frps -c frps.ini > /dev/null 2>&1 &
|
||||
|
||||
# 查看进程(确认启动成功)
|
||||
ps aux | grep frps
|
||||
|
||||
# 查看端口监听
|
||||
netstat -tlnp | grep 7000
|
||||
```
|
||||
|
||||
**成功输出示例**:
|
||||
```
|
||||
root 12345 0.0 0.1 12345 6789 ? Sl 20:XX 0:00 ./frps -c frps.ini
|
||||
tcp 0 0 0.0.0.0:7000 0.0.0.0:* LISTEN 12345/frps
|
||||
tcp 0 0 0.0.0.0:7500 0.0.0.0:* LISTEN 12345/frps
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.5 配置开机自启动(Systemd 服务)
|
||||
|
||||
```bash
|
||||
# 创建 systemd 服务文件
|
||||
nano /etc/systemd/system/frps.service
|
||||
```
|
||||
|
||||
**粘贴以下内容**:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=FRP Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=ubuntu
|
||||
Group=ubuntu
|
||||
WorkingDirectory=/opt/frp/frp_0.61.1_linux_amd64
|
||||
ExecStart=/opt/frp/frp_0.61.1_linux_amd64/frps -c /opt/frp/frp_0.61.1_linux_amd64/frps.ini
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**保存退出**(Ctrl+O → Enter → Ctrl+X)
|
||||
|
||||
```bash
|
||||
# 重新加载 systemd 配置
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 停止手动启动的 FRP
|
||||
sudo pkill frps
|
||||
|
||||
# 启用并启动服务
|
||||
sudo systemctl enable frps
|
||||
sudo systemctl start frps
|
||||
|
||||
# 查看服务状态
|
||||
sudo systemctl status frps
|
||||
```
|
||||
|
||||
**成功状态**:
|
||||
```
|
||||
● frps.service - FRP Server
|
||||
Loaded: loaded (/etc/systemd/system/frps.service; enabled; preset: enabled)
|
||||
Active: active (running) since Mon 2026-03-02 20:48:25 CST; 8s ago
|
||||
```
|
||||
|
||||
按 `Ctrl + C` 退出状态查看
|
||||
|
||||
---
|
||||
|
||||
### 3.6 验证服务端配置
|
||||
|
||||
```bash
|
||||
# 查看日志
|
||||
tail -f /opt/frp/frps.log
|
||||
|
||||
# 查看管理面板
|
||||
# 浏览器访问:http://你的服务器IP:7500
|
||||
# 用户名:admin,密码:Admin@2024Secure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、配置本地 FRP 客户端
|
||||
|
||||
### 4.1 下载 FRP Windows 客户端
|
||||
|
||||
#### 方法 1:浏览器下载
|
||||
|
||||
1. 访问:https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_windows_amd64.zip
|
||||
2. 下载到本地(如 `Downloads` 文件夹)
|
||||
|
||||
#### 方法 2:PowerShell 下载
|
||||
|
||||
```powershell
|
||||
# 创建目录
|
||||
mkdir C:\frp
|
||||
cd C:\frp
|
||||
|
||||
# 下载
|
||||
Invoke-WebRequest -Uri "https://github.com/fatedier/frp/releases/download/v0.61.1/frp_0.61.1_windows_amd64.zip" -OutFile "C:\frp\frp.zip"
|
||||
|
||||
# 解压
|
||||
Expand-Archive -Path "C:\frp\frp.zip" -DestinationPath "C:\frp" -Force
|
||||
|
||||
# 进入目录
|
||||
cd C:\frp\frp_0.61.1_windows_amd64
|
||||
|
||||
# 查看文件
|
||||
dir
|
||||
```
|
||||
|
||||
**应该看到**:
|
||||
```
|
||||
frpc.exe
|
||||
frps.exe
|
||||
frpc.toml
|
||||
frps.toml
|
||||
LICENSE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 配置 FRP 客户端
|
||||
|
||||
#### 步骤 1:创建配置文件
|
||||
|
||||
在 `C:\frp\frp_0.61.1_windows_amd64` 目录创建 `frpc.ini`:
|
||||
|
||||
```powershell
|
||||
notepad frpc.ini
|
||||
```
|
||||
|
||||
#### 步骤 2:粘贴配置
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr = 你的腾讯云公网 IP
|
||||
server_port = 7000
|
||||
token = 你的 token(和服务端一致)
|
||||
log_file = C:\frp\frpc.log
|
||||
log_level = info
|
||||
log_max_days = 7
|
||||
|
||||
[remote-desktop]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 3000
|
||||
remote_port = 8080
|
||||
```
|
||||
|
||||
**⚠️ 必须修改的参数**:
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| server_addr | 腾讯云公网 IP | 146.56.248.142 |
|
||||
| token | 和服务端一致的 token | Kj8Hn2bVx9... |
|
||||
| local_port | Remote 应用本地端口 | 3000 |
|
||||
| remote_port | 服务器访问端口 | 8080 |
|
||||
|
||||
#### 步骤 3:保存
|
||||
|
||||
保存为 `frpc.ini`(注意编码选择 UTF-8)
|
||||
|
||||
---
|
||||
|
||||
### 4.3 启动 FRP 客户端
|
||||
|
||||
```powershell
|
||||
# 在 C:\frp\frp_0.61.1_windows_amd64 目录
|
||||
.\frpc.exe -c frpc.ini
|
||||
```
|
||||
|
||||
**成功输出**:
|
||||
```
|
||||
start service success
|
||||
[remote-desktop] start proxy success
|
||||
```
|
||||
|
||||
> ⚠️ **注意**:会看到 `WARNING: ini format is deprecated...` 提示,可以忽略,不影响使用
|
||||
|
||||
---
|
||||
|
||||
### 4.4 配置开机自启动(Windows 服务)
|
||||
|
||||
#### 方法 1:创建批处理文件
|
||||
|
||||
创建 `C:\frp\install-service.bat`:
|
||||
|
||||
```batch
|
||||
@echo off
|
||||
sc create FRPClient binPath= "C:\frp\frp_0.61.1_windows_amd64\frpc.exe -c C:\frp\frp_0.61.1_windows_amd64\frpc.ini" start= auto
|
||||
sc description FRPClient "FRP Client for Remote Access"
|
||||
sc start FRPClient
|
||||
echo FRP Client service installed successfully!
|
||||
pause
|
||||
```
|
||||
|
||||
**以管理员身份运行**这个批处理文件
|
||||
|
||||
#### 方法 2:使用任务计划程序
|
||||
|
||||
1. 搜索"任务计划程序"
|
||||
2. 创建基本任务
|
||||
3. 触发器:开机时
|
||||
4. 操作:启动程序
|
||||
5. 程序:`C:\frp\frp_0.61.1_windows_amd64\frpc.exe`
|
||||
6. 参数:`-c C:\frp\frp_0.61.1_windows_amd64\frpc.ini`
|
||||
|
||||
---
|
||||
|
||||
## 五、启动和测试
|
||||
|
||||
### 5.1 启动顺序
|
||||
|
||||
**必须先启动 FRP 客户端,再启动 Remote 应用**
|
||||
|
||||
#### 步骤 1:启动 FRP 客户端
|
||||
|
||||
```powershell
|
||||
cd C:\frp\frp_0.61.1_windows_amd64
|
||||
.\frpc.exe -c frpc.ini
|
||||
```
|
||||
|
||||
保持这个窗口运行(不要关闭)
|
||||
|
||||
#### 步骤 2:启动 Remote 应用
|
||||
|
||||
**打开新的 PowerShell 窗口**:
|
||||
|
||||
```powershell
|
||||
cd C:\Users\xuanchi\Desktop\remote
|
||||
node .\index.js
|
||||
```
|
||||
|
||||
**成功输出**:
|
||||
```
|
||||
Remote screen streaming server started
|
||||
Encoder: mpeg1video
|
||||
WebSocket: ws://localhost:3000/ws
|
||||
HTTP: http://localhost:3000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.2 测试访问
|
||||
|
||||
#### 测试 1:本地访问
|
||||
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
|
||||
应该能看到 Remote 应用界面
|
||||
|
||||
#### 测试 2:外网访问
|
||||
|
||||
```
|
||||
http://你的腾讯云公网IP:8080
|
||||
```
|
||||
|
||||
应该能看到相同的 Remote 应用界面
|
||||
|
||||
#### 测试 3:FRP 管理面板
|
||||
|
||||
```
|
||||
http://你的腾讯云公网IP:7500
|
||||
```
|
||||
|
||||
**登录信息**:
|
||||
- 用户名:`admin`
|
||||
- 密码:`Admin@2024Secure`
|
||||
|
||||
**应该看到**:
|
||||
- 在线代理数量:1
|
||||
- [remote-desktop] 状态:在线
|
||||
|
||||
---
|
||||
|
||||
### 5.3 流量测试
|
||||
|
||||
```bash
|
||||
# 在服务器上查看流量
|
||||
watch -n 1 'netstat -an | grep 8080 | wc -l'
|
||||
```
|
||||
|
||||
或者在 FRP 管理面板查看实时流量统计
|
||||
|
||||
---
|
||||
|
||||
## 六、常见问题排查
|
||||
|
||||
### 6.1 FRP 客户端无法连接服务端
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
login to server failed: dial tcp xxx.xxx.xxx.xxx:7000: connectex: A connection attempt failed
|
||||
```
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. **检查服务器安全组**
|
||||
```bash
|
||||
# SSH 到服务器
|
||||
sudo ufw status
|
||||
# 或
|
||||
sudo iptables -L -n | grep 7000
|
||||
```
|
||||
|
||||
2. **检查 FRP 服务端状态**
|
||||
```bash
|
||||
sudo systemctl status frps
|
||||
sudo netstat -tlnp | grep 7000
|
||||
```
|
||||
|
||||
3. **检查 token 是否一致**
|
||||
```bash
|
||||
# 服务端
|
||||
cat /opt/frp/frp_0.61.1_linux_amd64/frps.ini | grep token
|
||||
|
||||
# 客户端
|
||||
type C:\frp\frp_0.61.1_windows_amd64\frpc.ini | findstr token
|
||||
```
|
||||
|
||||
4. **测试端口连通性**
|
||||
```powershell
|
||||
# 在本地 PowerShell
|
||||
Test-NetConnection -ComputerName 你的服务器IP -Port 7000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6.2 浏览器显示 ERR_UNSAFE_PORT
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
嗯... 无法访问此页面
|
||||
ERR_UNSAFE_PORT
|
||||
```
|
||||
|
||||
**原因**:Chrome/Edge 阻止了某些端口(如 6000、4045 等)
|
||||
|
||||
**解决方法**:
|
||||
1. 修改 FRP 客户端配置 `remote_port = 8080`(或其他安全端口)
|
||||
2. 修改腾讯云安全组规则
|
||||
3. 重启 FRP 客户端
|
||||
|
||||
**安全端口推荐**:8080、8000、5000、8888、3001
|
||||
|
||||
---
|
||||
|
||||
### 6.3 Remote 应用无法启动
|
||||
|
||||
**错误信息**:
|
||||
```
|
||||
Error: Cannot find module 'xxx'
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
```powershell
|
||||
cd C:\Users\xuanchi\Desktop\remote
|
||||
npm install
|
||||
node .\index.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6.4 连接成功但无画面
|
||||
|
||||
**可能原因**:
|
||||
1. Remote 应用未启动
|
||||
2. 端口映射错误
|
||||
3. 编码器问题
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. **检查本地访问**
|
||||
```
|
||||
http://localhost:3000
|
||||
```
|
||||
如果本地也无法访问,说明 Remote 应用未启动
|
||||
|
||||
2. **检查 FRP 日志**
|
||||
```powershell
|
||||
type C:\frp\frpc.log
|
||||
```
|
||||
|
||||
3. **检查 Remote 应用日志**
|
||||
查看 PowerShell 输出
|
||||
|
||||
---
|
||||
|
||||
### 6.5 延迟高/卡顿
|
||||
|
||||
**优化建议**:
|
||||
|
||||
1. **降低视频码率**
|
||||
```javascript
|
||||
// 修改 FFmpegEncoder.js
|
||||
this.bitrate = '800k'; // 从 1500k 降低到 800k
|
||||
```
|
||||
|
||||
2. **降低帧率**
|
||||
```javascript
|
||||
this.fps = 20; // 从 30 降低到 20
|
||||
```
|
||||
|
||||
3. **降低分辨率**
|
||||
```javascript
|
||||
// 修改 scale 参数
|
||||
'-vf', 'scale=1024:576:force_original_aspect_ratio=decrease',
|
||||
```
|
||||
|
||||
4. **选择更近的服务器地域**
|
||||
- 南方用户:广州/上海
|
||||
- 北方用户:北京
|
||||
- 西南用户:成都
|
||||
|
||||
---
|
||||
|
||||
### 6.6 FRP 服务意外停止
|
||||
|
||||
**检查 systemd 服务状态**:
|
||||
```bash
|
||||
sudo systemctl status frps
|
||||
sudo journalctl -u frps -n 50
|
||||
```
|
||||
|
||||
**重启服务**:
|
||||
```bash
|
||||
sudo systemctl restart frps
|
||||
```
|
||||
|
||||
**查看日志**:
|
||||
```bash
|
||||
tail -f /opt/frp/frps.log
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、优化建议
|
||||
|
||||
### 7.1 性能优化
|
||||
|
||||
#### 服务器端优化
|
||||
|
||||
```bash
|
||||
# 1. 更新系统
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
|
||||
# 2. 优化网络参数
|
||||
sudo nano /etc/sysctl.conf
|
||||
```
|
||||
|
||||
**添加以下配置**:
|
||||
```
|
||||
net.core.rmem_max = 16777216
|
||||
net.core.wmem_max = 16777216
|
||||
net.ipv4.tcp_rmem = 4096 87380 16777216
|
||||
net.ipv4.tcp_wmem = 4096 87380 16777216
|
||||
```
|
||||
|
||||
**应用配置**:
|
||||
```bash
|
||||
sudo sysctl -p
|
||||
```
|
||||
|
||||
#### 客户端优化
|
||||
|
||||
1. **使用有线网络**(比 WiFi 更稳定)
|
||||
2. **关闭后台占用带宽的应用**
|
||||
3. **使用 5GHz WiFi**(如果必须用无线)
|
||||
|
||||
---
|
||||
|
||||
### 7.2 安全加固
|
||||
|
||||
#### 修改默认端口
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
bind_port = 27000 # 不用默认的 7000
|
||||
dashboard_port = 27500 # 不用默认的 7500
|
||||
```
|
||||
|
||||
#### 启用 HTTPS 管理面板
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
dashboard_tls_mode = true
|
||||
dashboard_tls_cert_file = /path/to/cert.pem
|
||||
dashboard_tls_key_file = /path/to/key.pem
|
||||
```
|
||||
|
||||
#### 限制访问 IP
|
||||
|
||||
```ini
|
||||
# frps.ini
|
||||
allow_ports = 2000-3000,3001,4000-50000
|
||||
```
|
||||
|
||||
#### 定期更换 Token
|
||||
|
||||
建议每 3-6 个月更换一次 token
|
||||
|
||||
---
|
||||
|
||||
### 7.3 监控和告警
|
||||
|
||||
#### 监控 FRP 状态
|
||||
|
||||
```bash
|
||||
# 创建监控脚本
|
||||
nano /opt/frp/check_frps.sh
|
||||
```
|
||||
|
||||
**内容**:
|
||||
```bash
|
||||
#!/bin/bash
|
||||
if ! pgrep -x "frps" > /dev/null; then
|
||||
echo "FRPS is not running, restarting..."
|
||||
systemctl restart frps
|
||||
echo "FRPS restarted at $(date)" >> /opt/frp/frps_restart.log
|
||||
fi
|
||||
```
|
||||
|
||||
**添加定时任务**:
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
**添加**:
|
||||
```
|
||||
*/5 * * * * /bin/bash /opt/frp/check_frps.sh
|
||||
```
|
||||
|
||||
每 5 分钟检查一次 FRP 状态
|
||||
|
||||
---
|
||||
|
||||
### 7.4 备份配置
|
||||
|
||||
#### 备份服务端配置
|
||||
|
||||
```bash
|
||||
# 创建备份目录
|
||||
mkdir -p /opt/frp/backup
|
||||
|
||||
# 备份配置文件
|
||||
cp /opt/frp/frp_0.61.1_linux_amd64/frps.ini /opt/frp/backup/frps_$(date +%Y%m%d).ini
|
||||
cp /etc/systemd/system/frps.service /opt/frp/backup/
|
||||
```
|
||||
|
||||
#### 备份客户端配置
|
||||
|
||||
```powershell
|
||||
# PowerShell 备份
|
||||
Copy-Item C:\frp\frp_0.61.1_windows_amd64\frpc.ini `
|
||||
"C:\frp\backup\frpc_$(Get-Date -Format 'yyyyMMdd').ini"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5 成本优化
|
||||
|
||||
#### 1. 选择合适地域
|
||||
|
||||
- 一线城市(北京/上海/广州):价格稍高,网络质量好
|
||||
- 二线城市(成都/南京):价格稍低,覆盖特定区域
|
||||
|
||||
#### 2. 选择合适时长
|
||||
|
||||
- 按月购买:45 元/月
|
||||
- 按年购买:459 元/年(相当于 38.25 元/月,省 15%)
|
||||
|
||||
#### 3. 利用活动
|
||||
|
||||
- 腾讯云新用户优惠
|
||||
- 双 11、618 等促销活动
|
||||
- 学生优惠(如果有学生身份)
|
||||
|
||||
---
|
||||
|
||||
## 附录 A:完整配置清单
|
||||
|
||||
### 服务端配置
|
||||
|
||||
```ini
|
||||
# /opt/frp/frp_0.61.1_linux_amd64/frps.ini
|
||||
[common]
|
||||
bind_port = 7000
|
||||
bind_addr = 0.0.0.0
|
||||
token = 你的 token
|
||||
dashboard_port = 7500
|
||||
dashboard_user = admin
|
||||
dashboard_pwd = Admin@2024Secure
|
||||
log_file = /opt/frp/frps.log
|
||||
log_level = info
|
||||
log_max_days = 7
|
||||
```
|
||||
|
||||
### 客户端配置
|
||||
|
||||
```ini
|
||||
# C:\frp\frp_0.61.1_windows_amd64\frpc.ini
|
||||
[common]
|
||||
server_addr = 你的腾讯云 IP
|
||||
server_port = 7000
|
||||
token = 你的 token
|
||||
log_file = C:\frp\frpc.log
|
||||
log_level = info
|
||||
log_max_days = 7
|
||||
|
||||
[remote-desktop]
|
||||
type = tcp
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 3000
|
||||
remote_port = 8080
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录 B:常用命令速查
|
||||
|
||||
### 服务端命令
|
||||
|
||||
```bash
|
||||
# 查看 FRP 状态
|
||||
sudo systemctl status frps
|
||||
|
||||
# 重启 FRP
|
||||
sudo systemctl restart frps
|
||||
|
||||
# 查看日志
|
||||
tail -f /opt/frp/frps.log
|
||||
|
||||
# 查看进程
|
||||
ps aux | grep frps
|
||||
|
||||
# 查看端口
|
||||
netstat -tlnp | grep 7000
|
||||
|
||||
# 重启服务器
|
||||
sudo reboot
|
||||
```
|
||||
|
||||
### 客户端命令
|
||||
|
||||
```powershell
|
||||
# 启动 FRP 客户端
|
||||
.\frpc.exe -c frpc.ini
|
||||
|
||||
# 查看进程
|
||||
tasklist | findstr frpc
|
||||
|
||||
# 停止 FRP
|
||||
taskkill /F /IM frpc.exe
|
||||
|
||||
# 重启 FRP
|
||||
taskkill /F /IM frpc.exe; .\frpc.exe -c frpc.ini
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录 C:资源链接
|
||||
|
||||
- [FRP 官方 GitHub](https://github.com/fatedier/frp)
|
||||
- [FRP 官方文档](https://gofrp.org/)
|
||||
- [腾讯云官网](https://cloud.tencent.com/)
|
||||
- [Ubuntu 官方文档](https://ubuntu.com/server/docs)
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
恭喜你完成腾讯云 + OpenFRP 的配置!现在你的 Remote 应用已经可以从外网访问了。
|
||||
|
||||
### 关键要点回顾:
|
||||
|
||||
1. ✅ 选择 Ubuntu 24.04 镜像
|
||||
2. ✅ 配置安全组开放 7000、7500、8080 端口
|
||||
3. ✅ 服务端和客户端 token 必须一致
|
||||
4. ✅ 避免使用 6000 等不安全端口
|
||||
5. ✅ 配置开机自启动保证稳定性
|
||||
|
||||
### 下一步建议:
|
||||
|
||||
1. 测试外网访问稳定性
|
||||
2. 根据需要调整视频质量参数
|
||||
3. 配置监控和告警
|
||||
4. 定期备份配置文件
|
||||
|
||||
**如有问题,请查看"常见问题排查"章节或查阅 FRP 官方文档!**
|
||||
|
||||
---
|
||||
|
||||
*文档版本:v1.0*
|
||||
*更新时间:2026-03-02*
|
||||
*适用 FRP 版本:v0.61.1*
|
||||
638
remote/docs/指南/腾讯云域名与SSL证书配置指南.md
Normal file
638
remote/docs/指南/腾讯云域名与SSL证书配置指南.md
Normal file
@@ -0,0 +1,638 @@
|
||||
# 腾讯云域名与 SSL 证书配置指南
|
||||
|
||||
> 本指南详细讲解如何在腾讯云服务器上配置域名解析和 SSL 证书,实现 HTTPS 安全访问。
|
||||
>
|
||||
> **适用场景**:远程桌面 HTTPS 访问、WebSocket 安全连接、WebCodecs API 使用
|
||||
>
|
||||
> **预计耗时**:10 分钟
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
- [一、前置知识](#一前置知识)
|
||||
- [二、准备工作](#二准备工作)
|
||||
- [三、开放安全组端口](#三开放安全组端口)
|
||||
- [四、域名 DNS 解析配置](#四域名-dns-解析配置)
|
||||
- [五、安装 Nginx 和 Certbot](#五安装-nginx-和-certbot)
|
||||
- [六、配置 Nginx 反向代理](#六配置-nginx-反向代理)
|
||||
- [七、申请 SSL 证书](#七申请-ssl-证书)
|
||||
- [八、配置子域名(可选)](#八配置子域名可选)
|
||||
- [九、自签名证书配置](#九自签名证书配置)
|
||||
- [十、验证配置](#十验证配置)
|
||||
- [十一、常见问题](#十一常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 一、前置知识
|
||||
|
||||
### 1.1 什么是域名?
|
||||
|
||||
域名是互联网上服务器的地址,方便人类记忆,代替难记的 IP 地址。
|
||||
|
||||
| 概念 | 示例 | 说明 |
|
||||
|------|------|------|
|
||||
| 顶级域名 | `.com`、`.cn`、`.org` | 域名的最后一部分 |
|
||||
| 主域名 | `example.com` | 你购买的域名 |
|
||||
| 子域名 | `www.example.com`、`api.example.com` | 主域名下的分支 |
|
||||
|
||||
**域名解析**:将域名转换为 IP 地址的过程。通过 DNS(Domain Name System)服务器完成。
|
||||
|
||||
### 1.2 什么是 HTTP 和 HTTPS?
|
||||
|
||||
| 协议 | 说明 | 特点 |
|
||||
|------|------|------|
|
||||
| HTTP | 超文本传输协议 | 明文传输,不安全 |
|
||||
| HTTPS | HTTP + SSL/TLS | 加密传输,安全 |
|
||||
|
||||
**为什么需要 HTTPS?**
|
||||
- 数据加密:防止中间人窃听
|
||||
- 身份验证:确认服务器身份
|
||||
- 数据完整性:防止数据被篡改
|
||||
- 浏览器要求:WebCodecs、某些 API 必须使用 HTTPS
|
||||
|
||||
### 1.3 什么是 SSL/TLS 证书?
|
||||
|
||||
SSL/TLS 证书是数字证书,用于验证服务器身份并建立加密连接。
|
||||
|
||||
| 证书类型 | 说明 | 适用场景 |
|
||||
|---------|------|---------|
|
||||
| DV(域名验证) | 只验证域名所有权 | 个人网站、博客 |
|
||||
| OV(组织验证) | 验证组织身份 | 企业网站 |
|
||||
| EV(扩展验证) | 严格验证组织身份 | 金融、电商 |
|
||||
|
||||
**证书颁发机构(CA)**:受信任的证书签发机构,如 Let's Encrypt、DigiCert 等。
|
||||
|
||||
### 1.4 什么是 Nginx?
|
||||
|
||||
Nginx 是高性能的 Web 服务器和反向代理服务器。
|
||||
|
||||
**本教程中 Nginx 的作用**:
|
||||
- 反向代理:将外部请求转发到本地 FRP 端口(8080)
|
||||
- SSL 终止:处理 HTTPS 加密/解密
|
||||
- WebSocket 代理:支持远程桌面的 WebSocket 连接
|
||||
|
||||
```
|
||||
用户浏览器 → Nginx(443端口)→ FRP(8080端口)→ 本地电脑
|
||||
HTTPS解密 内网穿透
|
||||
```
|
||||
|
||||
### 1.5 什么是 Certbot?
|
||||
|
||||
Certbot 是 Let's Encrypt 官方推荐的证书申请工具。
|
||||
|
||||
**Certbot 的作用**:
|
||||
- 自动申请 SSL 证书
|
||||
- 自动配置 Nginx
|
||||
- 自动续期证书(Let's Encrypt 证书有效期 90 天)
|
||||
|
||||
**Let's Encrypt**:免费的证书颁发机构,由非营利组织 ISRG 运营。
|
||||
|
||||
### 1.6 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 腾讯云服务器 │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||
│ │ Nginx │────▶│ FRP │────▶│ 本地电脑 │ │
|
||||
│ │ :443 │ │ :8080 │ │ :3000 │ │
|
||||
│ │ HTTPS │ │ 内网穿透 │ │ 远程桌面 │ │
|
||||
│ └─────────┘ └─────────┘ └─────────┘ │
|
||||
│ ▲ │
|
||||
│ │ SSL证书 │
|
||||
│ ┌────┴────┐ │
|
||||
│ │ Certbot │ │
|
||||
│ │ Let's │ │
|
||||
│ │ Encrypt │ │
|
||||
│ └─────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
▲
|
||||
│ DNS解析
|
||||
┌─────┴─────┐
|
||||
│ 域名 │
|
||||
│ example. │
|
||||
│ com │
|
||||
└───────────┘
|
||||
```
|
||||
|
||||
**请求流程**:
|
||||
1. 用户访问 `https://example.com`
|
||||
2. DNS 解析到腾讯云服务器 IP
|
||||
3. Nginx 接收 HTTPS 请求,解密
|
||||
4. Nginx 反向代理到 FRP 的 8080 端口
|
||||
5. FRP 转发到本地电脑的 3000 端口
|
||||
6. 本地电脑返回远程桌面画面
|
||||
|
||||
---
|
||||
|
||||
## 二、准备工作
|
||||
|
||||
### 2.1 所需材料
|
||||
|
||||
| 项目 | 说明 |
|
||||
|------|------|
|
||||
| 腾讯云服务器 | 已配置好 FRP 服务端 |
|
||||
| 域名 | 已购买并完成实名认证 |
|
||||
| FRP 服务端口 | 默认 8080 |
|
||||
|
||||
### 2.2 端口规划
|
||||
|
||||
| 端口 | 用途 | 说明 |
|
||||
|------|------|------|
|
||||
| 80 | HTTP 访问 | Certbot 验证 + HTTP 重定向 |
|
||||
| 443 | HTTPS 访问 | 安全访问入口 |
|
||||
| 8080 | FRP 转发端口 | 远程桌面实际端口 |
|
||||
|
||||
---
|
||||
|
||||
## 三、开放安全组端口
|
||||
|
||||
> **重要:必须先开放端口才能继续后续配置!**
|
||||
|
||||
### 3.1 登录腾讯云控制台
|
||||
|
||||
1. 登录 [腾讯云控制台](https://console.cloud.tencent.com/)
|
||||
2. 进入「云服务器」→ 选择服务器 →「安全组」
|
||||
|
||||
### 3.2 添加入站规则
|
||||
|
||||
点击「添加规则」,添加以下两条规则:
|
||||
|
||||
| 协议 | 端口 | 来源 | 策略 |
|
||||
|------|------|------|------|
|
||||
| TCP | 80 | 0.0.0.0/0 | 允许 |
|
||||
| TCP | 443 | 0.0.0.0/0 | 允许 |
|
||||
|
||||
### 3.3 验证端口
|
||||
|
||||
在服务器上执行:
|
||||
|
||||
```bash
|
||||
sudo ufw allow 80/tcp
|
||||
sudo ufw allow 443/tcp
|
||||
sudo ufw status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、域名 DNS 解析配置
|
||||
|
||||
### 3.1 登录腾讯云 DNS 解析
|
||||
|
||||
1. 登录 [腾讯云控制台](https://console.cloud.tencent.com/)
|
||||
2. 进入「我的域名」→ 选择域名 →「解析」
|
||||
3. 点击「添加记录」
|
||||
|
||||
### 3.2 添加 A 记录
|
||||
|
||||
| 参数 | 值 |
|
||||
|------|-----|
|
||||
| 主机记录 | `@`(表示主域名本身) |
|
||||
| 记录类型 | `A` |
|
||||
| 记录值 | 腾讯云服务器公网 IP |
|
||||
| TTL | 默认(600) |
|
||||
|
||||
### 3.3 配置示例
|
||||
|
||||
假设你的域名是 `example.com`,服务器 IP 是 `123.45.67.89`:
|
||||
|
||||
| 主机记录 | 完整域名 | 说明 |
|
||||
|---------|---------|------|
|
||||
| `@` | `example.com` | 主域名 |
|
||||
| `www` | `www.example.com` | www 子域名 |
|
||||
|
||||
### 3.4 验证 DNS 解析
|
||||
|
||||
在本地电脑或服务器上执行:
|
||||
|
||||
```bash
|
||||
ping example.com
|
||||
```
|
||||
|
||||
如果返回你的服务器 IP,说明解析生效。
|
||||
|
||||
---
|
||||
|
||||
## 四、安装 Nginx 和 Certbot
|
||||
|
||||
### 4.1 SSH 连接服务器
|
||||
|
||||
```bash
|
||||
ssh ubuntu@你的服务器公网IP
|
||||
```
|
||||
|
||||
### 4.2 安装软件
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y nginx certbot python3-certbot-nginx
|
||||
```
|
||||
|
||||
### 4.3 验证安装
|
||||
|
||||
```bash
|
||||
nginx -v
|
||||
# 输出:nginx version: nginx/1.24.0
|
||||
|
||||
certbot --version
|
||||
# 输出:certbot 2.x.x
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、配置 Nginx 反向代理
|
||||
|
||||
### 5.1 创建站点配置
|
||||
|
||||
将 `example.com` 替换为你的实际域名:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/nginx/sites-available/example.com << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com www.example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
> **说明**:配置文件名使用域名命名,方便管理。将 `example.com` 替换为你自己的域名。
|
||||
|
||||
### 5.2 启用站点配置
|
||||
|
||||
```bash
|
||||
# 创建软链接
|
||||
sudo ln -sf /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
|
||||
|
||||
# 删除默认站点(可选)
|
||||
sudo rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# 测试配置
|
||||
sudo nginx -t
|
||||
```
|
||||
|
||||
### 5.3 重载 Nginx
|
||||
|
||||
```bash
|
||||
sudo systemctl reload nginx
|
||||
sudo systemctl status nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、申请 SSL 证书
|
||||
|
||||
### 6.1 使用 Certbot 申请
|
||||
|
||||
将 `example.com` 替换为你的实际域名:
|
||||
|
||||
```bash
|
||||
sudo certbot --nginx -d example.com -d www.example.com
|
||||
```
|
||||
|
||||
### 6.2 按提示操作
|
||||
|
||||
```
|
||||
Enter email address: your_email@example.com
|
||||
# 输入邮箱,用于接收证书到期提醒
|
||||
|
||||
Please read the Terms of Service: (Y)es/(N)o: Y
|
||||
# 同意服务条款
|
||||
|
||||
Would you be willing to share your email address: (Y)es/(N)o: N
|
||||
# 是否分享邮箱,选 N
|
||||
```
|
||||
|
||||
### 6.3 选择 HTTPS 重定向
|
||||
|
||||
```
|
||||
Please choose whether or not to redirect HTTP to HTTPS:
|
||||
1: No redirect - No further changes to the webserver configuration.
|
||||
2: Redirect - Make all requests redirect to secure HTTPS access.
|
||||
Select the appropriate number: 2
|
||||
# 选择 2,自动将 HTTP 重定向到 HTTPS
|
||||
```
|
||||
|
||||
### 6.4 成功提示
|
||||
|
||||
```
|
||||
Successfully received certificate.
|
||||
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
|
||||
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
|
||||
This certificate expires on 2026-06-01.
|
||||
```
|
||||
|
||||
### 6.5 重要提示
|
||||
|
||||
> **注意**:Let's Encrypt 有频率限制,每小时最多 5 次失败尝试。如果多次失败会被临时封禁 1 小时。
|
||||
>
|
||||
> 如果遇到封禁,请使用「九、自签名证书配置」作为临时方案。
|
||||
|
||||
---
|
||||
|
||||
## 七、配置子域名(可选)
|
||||
|
||||
> 如果你需要为不同服务配置不同的子域名,请参考本章节。
|
||||
|
||||
### 7.1 添加 DNS 解析
|
||||
|
||||
在腾讯云控制台添加新的 A 记录:
|
||||
|
||||
| 主机记录 | 记录类型 | 记录值 |
|
||||
|---------|---------|--------|
|
||||
| `remote` | A | 服务器 IP |
|
||||
| `api` | A | 服务器 IP |
|
||||
| `file` | A | 服务器 IP |
|
||||
|
||||
### 7.2 创建子域名 Nginx 配置
|
||||
|
||||
以 `remote.example.com` 子域名为例:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/nginx/sites-available/remote.example.com << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name remote.example.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
sudo ln -sf /etc/nginx/sites-available/remote.example.com /etc/nginx/sites-enabled/
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 7.3 申请子域名证书
|
||||
|
||||
```bash
|
||||
sudo certbot --nginx -d remote.example.com
|
||||
```
|
||||
|
||||
### 7.4 子域名用途示例
|
||||
|
||||
| 子域名 | 用途 |
|
||||
|--------|------|
|
||||
| `remote.example.com` | 远程桌面 |
|
||||
| `api.example.com` | API 服务 |
|
||||
| `file.example.com` | 文件服务 |
|
||||
|
||||
---
|
||||
|
||||
## 八、自签名证书配置
|
||||
|
||||
> 当 Let's Encrypt 封禁或测试环境时使用
|
||||
|
||||
### 8.1 生成自签名证书
|
||||
|
||||
将 `example.com` 替换为你的实际域名:
|
||||
|
||||
```bash
|
||||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /etc/ssl/private/example.com.key \
|
||||
-out /etc/ssl/certs/example.com.crt \
|
||||
-subj "/CN=example.com"
|
||||
```
|
||||
|
||||
### 8.2 配置 Nginx 使用自签名证书
|
||||
|
||||
将 `example.com` 替换为你的实际域名:
|
||||
|
||||
```bash
|
||||
sudo tee /etc/nginx/sites-available/example.com << 'EOF'
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com www.example.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name example.com www.example.com;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/example.com.crt;
|
||||
ssl_certificate_key /etc/ssl/private/example.com.key;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
sudo nginx -t && sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 8.3 浏览器访问
|
||||
|
||||
访问 `https://example.com`(替换为你的域名),浏览器会提示"不安全":
|
||||
1. 点击「高级」
|
||||
2. 点击「继续访问」即可使用
|
||||
|
||||
---
|
||||
|
||||
## 九、验证配置
|
||||
|
||||
### 9.1 检查端口监听
|
||||
|
||||
```bash
|
||||
sudo netstat -tlnp | grep -E '80|443'
|
||||
```
|
||||
|
||||
期望输出:
|
||||
|
||||
```
|
||||
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* nginx
|
||||
tcp LISTEN 0 511 0.0.0.0:443 0.0.0.0:* nginx
|
||||
```
|
||||
|
||||
### 9.2 测试 HTTPS 访问
|
||||
|
||||
在本地浏览器访问:
|
||||
|
||||
```
|
||||
https://example.com
|
||||
```
|
||||
|
||||
如果看到小锁图标,说明 HTTPS 配置成功。
|
||||
|
||||
### 9.3 测试 WebSocket
|
||||
|
||||
打开浏览器控制台(F12),检查 WebSocket 连接:
|
||||
|
||||
```
|
||||
WebSocket connected
|
||||
```
|
||||
|
||||
### 9.4 检查证书自动续期
|
||||
|
||||
```bash
|
||||
sudo certbot renew --dry-run
|
||||
```
|
||||
|
||||
如果显示 `Congratulations, all simulated renewals succeeded`,说明自动续期配置正常。
|
||||
|
||||
---
|
||||
|
||||
## 十一、常见问题
|
||||
|
||||
### 11.1 证书申请失败:DNS 解析未生效
|
||||
|
||||
**错误信息**:
|
||||
|
||||
```
|
||||
Failed to verify domain: DNS problem: NXDOMAIN looking up A for example.com
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
|
||||
等待 DNS 解析生效(通常需要几分钟),然后重试:
|
||||
|
||||
```bash
|
||||
sudo certbot --nginx -d example.com
|
||||
```
|
||||
|
||||
### 11.2 证书申请失败:频率限制
|
||||
|
||||
**错误信息**:
|
||||
|
||||
```
|
||||
too many failed authorizations (5) for "example.com" in the last 1h0m0s
|
||||
```
|
||||
|
||||
**解决方法**:
|
||||
|
||||
Let's Encrypt 封禁 1 小时,使用自签名证书临时替代:
|
||||
|
||||
```bash
|
||||
# 参考第八章「自签名证书配置」
|
||||
```
|
||||
|
||||
### 11.3 Nginx 502 Bad Gateway
|
||||
|
||||
**原因**:FRP 服务未运行或端口不正确
|
||||
|
||||
**解决方法**:
|
||||
|
||||
```bash
|
||||
# 检查 FRP 服务端是否运行
|
||||
sudo netstat -tlnp | grep 8080
|
||||
|
||||
# 如果没有运行,启动 FRP 服务端
|
||||
sudo systemctl start frps
|
||||
```
|
||||
|
||||
### 11.4 WebSocket 连接失败
|
||||
|
||||
**原因**:Nginx 未正确配置 WebSocket 代理
|
||||
|
||||
**解决方法**:
|
||||
|
||||
确保 Nginx 配置包含以下内容:
|
||||
|
||||
```nginx
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
```
|
||||
|
||||
### 11.5 证书过期
|
||||
|
||||
Let's Encrypt 证书有效期为 90 天,Certbot 会自动续期。
|
||||
|
||||
手动续期:
|
||||
|
||||
```bash
|
||||
sudo certbot renew
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### 11.6 如何删除证书
|
||||
|
||||
```bash
|
||||
# 查看证书列表
|
||||
sudo certbot certificates
|
||||
|
||||
# 删除指定证书
|
||||
sudo certbot delete --cert-name example.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 附录:完整配置文件
|
||||
|
||||
### Nginx 站点配置(SSL 配置后)
|
||||
|
||||
将 `example.com` 替换为你的实际域名:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com www.example.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name example.com www.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_read_timeout 86400;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 步骤 | 命令/操作 |
|
||||
|------|----------|
|
||||
| 1. 开放端口 | 腾讯云安全组开放 80、443(必须先做!) |
|
||||
| 2. DNS 解析 | 腾讯云控制台添加 A 记录 |
|
||||
| 3. 安装软件 | `sudo apt install -y nginx certbot python3-certbot-nginx` |
|
||||
| 4. 配置 Nginx | 创建 `/etc/nginx/sites-available/你的域名` |
|
||||
| 5. 申请证书 | `sudo certbot --nginx -d 你的域名` |
|
||||
| 6. 验证 | 浏览器访问 `https://你的域名` |
|
||||
|
||||
**配置完成后**:
|
||||
- ✅ 支持 HTTPS 安全访问
|
||||
- ✅ 支持 WebSocket 安全连接(WSS)
|
||||
- ✅ 支持 WebCodecs API
|
||||
- ✅ 证书自动续期
|
||||
|
||||
---
|
||||
|
||||
*文档版本:v1.3*
|
||||
*更新时间:2026-03-03*
|
||||
*适用系统:Ubuntu 24.04 LTS*
|
||||
Reference in New Issue
Block a user