|
|
# 通信协议
|
|
|
|
|
|
本文档详细介绍 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
|