You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
11 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 通信协议
本文档详细介绍 Flash Send 使用的网络通信协议。
## 概述
Flash Send 使用三种协议进行通信:
| 协议 | 端口 | 用途 | 加密 |
|------|------|------|------|
| UDP | 53317 | 设备发现 | 无 |
| WebSocket | 53318 | 即时聊天 | 无(局域网) |
| HTTPS | 53319 | 文件传输 | TLS |
## 设备发现协议
### 广播机制
使用 UDP 广播在局域网内发现设备。
**广播地址**: `255.255.255.255:53317`
**广播频率**: 每 3 秒一次
### 消息格式
所有消息使用 JSON 格式,以换行符 `\n` 结尾。
#### 广播消息 (Announce)
设备向局域网广播自身信息:
```json
{
"type": "announce",
"device": {
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"device_name": "My Computer",
"ip": "192.168.1.100",
"ws_port": 53318,
"http_port": 53319,
"last_seen": 1701388800
}
}
```
#### 响应消息 (Response)
收到广播后,设备回复自身信息:
```json
{
"type": "response",
"device": {
"device_id": "661e9500-f30c-52e5-b827-557766551111",
"device_name": "Other PC",
"ip": "192.168.1.101",
"ws_port": 53318,
"http_port": 53319,
"last_seen": 1701388805
}
}
```
### 设备状态
- **在线**: 每 3 秒收到广播/响应
- **离线**: 超过 15 秒未收到任何消息
### 流程图
```
┌─────────────┐ ┌─────────────┐
│ Device A │ │ Device B │
└──────┬──────┘ └──────┬──────┘
│ │
│ UDP Broadcast (announce) │
│ 255.255.255.255:53317 │
│ ─────────────────────────────────────────▶ │
│ │
│ UDP Response │
│ ◀───────────────────────────────────────── │
│ │
│ Device A adds Device B to list │
│ │
│ Device B adds │
│ Device A to list │
│ │
```
---
## WebSocket 聊天协议
### 连接建立
1. 客户端连接到目标设备的 WebSocket 服务
2. 发送握手消息
3. 服务端确认后,连接建立
**连接地址**: `ws://{ip}:{ws_port}`
### 消息格式
所有消息使用 JSON 格式。
#### 握手消息 (Handshake)
客户端首先发送握手消息:
```json
{
"type": "handshake",
"device_id": "550e8400-e29b-41d4-a716-446655440000",
"device_name": "My Computer"
}
```
#### 聊天消息 (Chat)
```json
{
"type": "chat",
"message": {
"id": "msg-uuid-12345",
"from_device": "550e8400-e29b-41d4-a716-446655440000",
"to_device": "661e9500-f30c-52e5-b827-557766551111",
"content": "Hello, World!",
"message_type": "text",
"timestamp": 1701388800000
}
}
```
**message_type 枚举**:
- `text`: 普通文本
- `image`: 图片content 为 base64 或路径)
- `file`: 文件content 为文件名)
- `system`: 系统消息
#### 确认消息 (Ack)
```json
{
"type": "ack",
"message_id": "msg-uuid-12345"
}
```
#### 心跳消息 (Ping/Pong)
```json
{
"type": "ping"
}
```
```json
{
"type": "pong"
}
```
### 连接管理
- **心跳间隔**: 30 秒
- **超时断开**: 90 秒无响应
- **自动重连**: 断开后尝试重连
### 流程图
```
┌─────────────┐ ┌─────────────┐
│ Client │ │ Server │
└──────┬──────┘ └──────┬──────┘
│ │
│ WebSocket Connect │
│ ─────────────────────────────────────────▶ │
│ │
│ Handshake │
│ ─────────────────────────────────────────▶ │
│ │
│ Connection OK │
│ ◀───────────────────────────────────────── │
│ │
│ Chat Message │
│ ─────────────────────────────────────────▶ │
│ │
│ Ack │
│ ◀───────────────────────────────────────── │
│ │
│ Ping │
│ ─────────────────────────────────────────▶ │
│ │
│ Pong │
│ ◀───────────────────────────────────────── │
│ │
```
---
## HTTPS 文件传输协议
### TLS 配置
- **证书类型**: 自签名 RSA 2048
- **证书有效期**: 365 天
- **客户端验证**: 跳过(局域网内互信)
### 端点
#### POST /upload
上传文件到服务端。
**Content-Type**: `multipart/form-data`
**表单字段**:
| 字段 | 类型 | 说明 |
|------|------|------|
| file_id | string | 文件传输 ID |
| from_device | string | 发送方设备 ID |
| file | binary | 文件内容 |
**请求示例**:
```http
POST /upload HTTP/1.1
Host: 192.168.1.100:53319
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Length: 12345
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file_id"
550e8400-e29b-41d4-a716-446655440000
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="from_device"
661e9500-f30c-52e5-b827-557766551111
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="document.pdf"
Content-Type: application/pdf
<binary data>
------WebKitFormBoundary7MA4YWxkTrZu0gW--
```
**响应**:
```http
HTTP/1.1 200 OK
Content-Type: application/json
```
#### GET /download/{file_id}
从服务端下载文件(预留接口)。
### 传输流程
```
┌─────────────┐ ┌─────────────┐
│ Sender │ │ Receiver │
└──────┬──────┘ └──────┬──────┘
│ │
│ 1. Create FileTransfer record │
│ (status: pending) │
│ │
│ 2. TLS Handshake │
│ ─────────────────────────────────────────▶ │
│ │
│ 3. POST /upload (multipart) │
│ ─────────────────────────────────────────▶ │
│ [chunk 1] │
│ emit progress event │
│ ─────────────────────────────────────────▶ │
│ [chunk 2] │ 4. Save to disk
│ emit progress event │ emit progress event
│ ─────────────────────────────────────────▶ │
│ [chunk n] │
│ emit progress event │
│ │
│ HTTP 200 OK │
│ ◀───────────────────────────────────────── │
│ │
│ 5. Update status: completed │ 5. Update status: completed
│ emit final progress event │ emit final progress event
│ │
```
### 分块传输
大文件使用分块传输,每块大小为 **64KB**
**进度计算**:
```
progress = transferred_bytes / total_bytes
```
**进度事件**:
- 每发送一个块后触发
- 包含当前进度、已传输字节数、总字节数
### 取消机制
1. 发送方调用 `cancel_transfer`
2. 触发 CancellationToken
3. 上传循环检测到取消,中断传输
4. 更新状态为 `cancelled`
5. 发送进度事件通知前端
---
## 安全考虑
### 局域网信任模型
Flash Send 假设局域网内的设备是可信的:
- UDP 广播不加密(仅设备发现)
- WebSocket 不加密(聊天内容)
- HTTP 使用 TLS文件传输
### TLS 证书
- 应用首次启动时生成自签名证书
- 证书存储在用户数据目录
- 客户端跳过证书验证(`dangerous_configuration`
### 建议
如果需要更高安全性:
1. 添加设备配对/授权机制
2. 使用端到端加密
3. 添加消息签名验证
---
## 端口冲突处理
如果默认端口被占用:
1. **检测**: 启动时检查端口可用性
2. **提示**: 通知用户端口冲突
3. **配置**: 允许用户在设置中修改端口
**端口范围建议**: 53317 - 53399