# 通信协议 本文档详细介绍 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 ------WebKitFormBoundary7MA4YWxkTrZu0gW-- ``` **响应**: ```http HTTP/1.1 200 OK Content-Type: application/json { "success": true, "file_id": "550e8400-e29b-41d4-a716-446655440000", "file_name": "document.pdf", "file_size": 12345 } ``` #### 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