# sync-local
**Repository Path**: hefy27/sync-local
## Basic Information
- **Project Name**: sync-local
- **Description**: 无话可说,懂得都懂!
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-04-07
- **Last Updated**: 2026-06-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
SyncLocal
轻量级 Windows 文件实时同步工具
本地双向同步 · 纯 HTTP P2P 网络传输 · 分片断点续传 · 系统托盘常驻
---
## 功能概览
### 文件同步
| 功能 | 说明 |
|------|------|
| 实时监听 | watchdog 监听文件创建、修改、删除、移动事件 |
| 同步模式 | 双向同步(A↔B)/ 单向 源→目标 / 单向 目标→源 |
| 多目录对 | 支持多组目录映射,各对独立启停 |
| 初始同步 | 启动时全量比对,三种策略:新版本优先 / 源优先 / 目标优先 |
| 手动同步 | 右键选中目录对触发全量比对 |
| 防抖合并 | 高频事件在可配置窗口内(默认 500ms,最小 100ms)合并处理 |
| 同步延迟 | 变更后额外等待 N 毫秒再同步 |
| 大文件跳过 | 超过指定大小的文件直接跳过(0 = 不限) |
| 同步删除 | 每对可独立开关是否同步删除操作 |
| 忽略规则 | 全局 + 每对独立的 glob 模式过滤 |
### 比对与冲突
| 功能 | 说明 |
|------|------|
| 快速比对 | size + mtime 相同即跳过 |
| MD5 校验 | >10MB 文件用 MD5 哈希精确比对 |
| 冲突检测 | 两端同时修改同一文件(时间差 <5s 且非近期同步产物)→ 创建 `.conflict_*` 副本 |
| 写入稳定性检测 | 两次采样(0.5s 间隔)确认文件大小稳定后再同步 |
| 文件锁检测 | 覆盖前检测目标是否被锁定,Windows 下可报告占用进程名(Restart Manager) |
| 失败重试 | 锁定/权限/写入中的操作自动排队重试(10s 间隔,最多 3 次) |
| 回环抑制 | 同步写入后短时间内抑制对端事件,防止循环同步 |
### 网络 P2P 同步
| 功能 | 说明 |
|------|------|
| 纯 HTTP 架构 | aiohttp 服务器 + http.client 客户端,双向 POST 通信 |
| 配对认证 | 6 位配对码首次连接验证,通过后自动加入信任列表 |
| 局域网发现 | UDP 广播(端口 39877)自动发现同网段实例,已配对的自动连接 |
| 分片传输 | 文件按可配置分片大小(64KB~64MB)独立传输,失败仅重传当前分片 |
| 断点续传 | partial 文件 + Content-Range,中断后自动从已接收位置继续 |
| MD5 完整性校验 | 传输完成后接收端校验 MD5,发送端确认校验结果 |
| 心跳保活 | 双向独立心跳(15s),失败指数退避(1s→2s→4s→8s→15s) |
| 两级断连阈值 | 连续 5 次失败 → UI 标记断连;连续 10 次失败 → 关闭连接 |
| 变更队列 | 断连期间变更暂存(最多 10000 条),重连后自动回放 |
| TLS 加密 | 可选自签名 HTTPS(RSA 2048,10 年有效期) |
| 并发控制 | 最大并发传输数可配置(1~10,默认 1) |
| 对端目录浏览 | 编辑同步对时可直接浏览远端目录树 |
### 隧道穿透
| 功能 | 说明 |
|------|------|
| Cloudflare Tunnel | 一键配置(API Token → 选域名 → 建隧道 → DNS),支持自启动 |
| ngrok | API Key 验证后一键启动,支持自启动 |
| 互斥运行 | 同一时间只能启用一种隧道 |
### 桌面体验
| 功能 | 说明 |
|------|------|
| 系统托盘 | 四态图标(蓝=同步中 / 绿=空闲 / 橙=暂停 / 灰=停止),悬浮显示详细状态 |
| Windows 通知 | 托盘 toast 通知(目录不可达、冲突等) |
| 配置界面 | 5 个选项卡:配置 / 网络 / 隧道 / 日志 / 统计 |
| 即时生效 | Ctrl+S 保存后自动重载引擎,分片大小与并发数即时应用到活跃连接 |
| 日志面板 | 关键词搜索 + 级别过滤(ERROR/WARN/INFO)+ 目录对过滤,最多 5000 行缓冲 |
| 统计面板 | 同步/跳过/冲突/错误计数 + 数据量统计 + 最近 100 条历史记录(持久化 SQLite) |
| 拖拽支持 | tkinterdnd2 可选支持拖拽目录到浏览器 |
### 可靠性
| 功能 | 说明 |
|------|------|
| 分片原子写入 | 每个分片先写唯一临时文件,完整接收后幂等合并到 partial 文件 |
| 自适应传输超时 | 根据分片大小动态计算(15s~60s) |
| 上传失败重入队列 | 1 次失败即重入队列,不阻塞其他文件 |
| 目录健康检查 | 每 15 秒检测监听目录是否可达,不可达时托盘告警 |
| 单实例保护 | Windows Mutex 防止重复启动 |
| 路径安全 | 目标路径不超出同步根目录,父子目录重叠检测 |
| 自连接防护 | 检测对端地址是否为本机(含隧道地址),跳过自连接 |
| 网络去重 | 相同变更在可配置窗口内(默认 2s)去重 |
---
## 快速开始
### 环境要求
- Windows 10+
- Python 3.12+
### 安装 & 运行
```bash
git clone
cd sync-local
pip install -r requirements.txt
python main.py
```
### 打包为 exe
```bash
pip install pyinstaller
bat\build.bat
```
产出 `dist/SyncLocal.exe`,无需 Python 环境。也可 `bat\start.bat` 后台启动(pythonw)。
---
## 使用指南
### 添加本地同步对
启动后弹出配置窗口 → 点击"添加" → 选择源目录和目标目录 → 设置同步模式 → 确定。
### 局域网 P2P 同步
1. **电脑 A**:切换到"网络"选项卡 → 点击"生成配对码" → 记下地址和配对码
2. **电脑 B**:输入电脑 A 的地址和配对码 → 点击"连接"
3. 配对成功后自动信任,同名同步对建立传输通道
4. 之后同局域网内自动发现、自动连接
### 公网同步
1. 切换到"隧道"选项卡
2. 选择 Cloudflare Tunnel 或 ngrok
3. 配置凭证并启动隧道
4. 对端使用隧道地址连接
---
## 配置参考
配置文件 `sync_config.json` 自动生成在程序目录下,所有项均可通过 UI 修改。
### 全局配置
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `auto_start_sync` | bool | `false` | 启动时自动开始同步 |
| `start_minimized` | bool | `false` | 启动时最小化到托盘 |
| `sync_interval_ms` | int | `500` | 防抖间隔(ms,≥100) |
| `max_file_size_mb` | int | `0` | 跳过超过此大小的文件(MB,0=不限) |
| `sync_delay_ms` | int | `0` | 变更后额外等待(ms,0=无) |
| `network_port` | int | `39876` | HTTP 监听端口(≥1024) |
| `lan_discoverable` | bool | `true` | 允许 UDP 局域网发现 |
| `connection_idle_timeout_s` | int | `180` | 空闲连接断开时间(秒) |
| `max_concurrent_transfers` | int | `1` | 最大并发传输数(1~10) |
| `upload_chunk_size_kb` | int | `4096` | 分片大小(KB,64~65536) |
| `enable_tls` | bool | `false` | 启用 TLS 加密 |
| `net_dedup_window_s` | float | `2.0` | 网络变更去重窗口(秒) |
| `manual_peers` | array | `[]` | 手动指定对端地址列表 |
| `ignore_patterns` | array | 见下 | 全局忽略规则 |
### 目录对配置
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `name` | string | - | 同步对名称 |
| `dir_source` / `dir_target` | string | - | 源目录 / 目标目录 |
| `sync_mode` | string | `"bidirectional"` | `bidirectional` / `source_to_target` / `target_to_source` |
| `sync_delete` | bool | `true` | 是否同步删除 |
| `enabled` | bool | `true` | 是否启用 |
| `initial_sync_strategy` | string | `"newer_wins"` | `newer_wins` / `source_wins` / `target_wins` |
| `network_enabled` | bool | `false` | 启用网络同步 |
| `ignore_patterns` | array | `[]` | 目录对专属忽略规则(glob) |
### 默认忽略规则
```
*.tmp *.log ~$* .DS_Store Thumbs.db desktop.ini __pycache__ .git .svn node_modules
```
---
## 架构
```
┌──────────────────────────────────────────────┐
│ 用户层 (UI) │
│ TrayApp · ConfigWindow · 5 Tabs · Dialogs │
└──────────────────┬───────────────────────────┘
│ 回调
┌──────────────────┴───────────────────────────┐
│ 协调层 (main.py) │
│ SyncLocalApp │
└───┬────────────────┬─────────────────────┬───┘
│ │ │
┌───┴───┐ ┌───────┴──────┐ ┌──────┴──────┐
│Engine │ │ConfigManager │ │ NetPeer │
│ Core │ │ Config │ │ Network │
└───┬───┘ └──────────────┘ └──────┬──────┘
│ │
│ watchdog │ aiohttp + http.client
│ threading │ asyncio + UDP
└───┴──────────────────────────────────────┴───┘
OS / 文件系统
```
### 网络传输模型
两个 peer 各运行 aiohttp HTTP 服务器,互为客户端:
```
┌──────────┐ POST /msg ┌──────────┐
│ Peer A │ ────────────────────────> │ Peer B │
│ :39876 │ <──────────────────────── │ :39876 │
└──────────┘ POST /msg └──────────┘
│ │
│ POST /transfer/upload (分片) │
│ ──────────────────────────────────> │
│ 200 {ok, offset/done, md5} │
│ <────────────────────────────────── │
```
### HTTP 端点
| 端点 | 用途 |
|------|------|
| `POST /auth` | 配对码认证 + 连接建立 |
| `POST /msg` | 统一控制消息通道 |
| `POST /heartbeat` | 心跳保活 |
| `POST /transfer/upload` | 分片文件上传(Content-Range) |
| `POST /transfer/download` | 分片文件下载(Range) |
| `POST /list-dir` | 远端目录列表 |
| `POST /health` | 健康检查 |
### 控制消息类型
| 消息 | 用途 |
|------|------|
| `file_notify` | 通知对端有文件需要传输 |
| `transfer_ready` | 接收端准备就绪,返回 token 和已有偏移量 |
| `transfer_done` | 传输完成确认 |
| `transfer_cancel` | 取消传输并清理 |
| `file_delete` | 删除文件通知 |
| `file_move` | 移动文件通知 |
| `file_check` / `file_check_resp` | 初始同步文件校验 |
| `initial_sync_done` | 初始扫描完成通知 |
| `pair_config_push` / `pair_config_ack` | 目录对配置同步 |
| `tunnel_addr` | 隧道地址通知 |
### 分片传输流程
```
发送端 接收端
│ file_notify (size, md5) │
│ ─────────────────────────────────> │
│ transfer_ready (token, offset) │ ← 检查 partial,返回已有偏移
│ <───────────────────────────────── │
│ │
│ 分片 N: POST /upload │
│ Content-Range: bytes X-Y/total │ → 写入唯一临时文件
│ ─────────────────────────────────> │ → 幂等合并到 partial
│ {ok, offset} │
│ <───────────────────────────────── │
│ ... │
│ 最后分片: POST /upload │
│ ─────────────────────────────────> │ → MD5 校验全文件
│ {ok, done, md5} │ → move 到目标目录
│ <───────────────────────────────── │
```
---
## 项目结构
```
sync-local/
├── main.py # 入口:SyncLocalApp + CLI + 单实例
│
├── core/
│ ├── models.py # 数据模型: SyncPair, AppConfig, SyncStats
│ ├── config.py # ConfigManager: JSON 配置读写
│ ├── engine.py # SyncEngine: 同步编排 + 防抖队列 + 初始同步
│ ├── handler.py # SyncHandler: watchdog 事件 → 同步动作
│ ├── conflict.py # ConflictResolver: 冲突检测 + .conflict 副本
│ ├── file_ops.py # FileOperations: MD5 / 锁检测 / 安全删除
│ ├── retry.py # RetryScheduler: 失败重试队列
│ ├── sync_service.py # SyncService: UI ↔ 核心中间层
│ └── database.py # SyncDatabase: SQLite 统计持久化
│
├── network/
│ ├── protocol.py # 协议常量: 消息类型 + 网络参数
│ ├── discovery.py # PeerDiscovery: UDP 局域网发现
│ ├── connection.py # PeerConnection: 单对端连接 (心跳/消息/传输)
│ ├── peer.py # NetPeer: 网络门面 (HTTP Server + Client)
│ ├── http_transfer.py # HttpTransferHandler: 上传/下载/目录 HTTP 端点
│ ├── transfer_manager.py # TransferManager: 传输令牌 + partial 文件管理
│ ├── change_queue.py # ChangeQueue: 断连变更暂存
│ ├── tls.py # TLS 自签名证书
│ ├── tunnel.py # Cloudflare Tunnel 管理
│ └── ngrok_tunnel.py # ngrok 隧道管理
│
├── ui/
│ ├── tray.py # TrayApp: 系统托盘四态图标
│ ├── main_window.py # ConfigWindow: 主窗口 + Notebook
│ ├── config_tab.py # 同步配置选项卡
│ ├── network_tab.py # 网络连接选项卡
│ ├── tunnel_tab.py # 隧道管理选项卡
│ ├── log_tab.py # 日志选项卡
│ ├── stats_tab.py # 统计选项卡
│ └── dialogs.py # PairDialog + DirBrowser
│
├── tests/ # pytest 单元测试
├── doc/ # 架构文档
├── bat/ # build.bat + start.bat
└── requirements.txt
```
## 技术栈
| 组件 | 用途 |
|------|------|
| [watchdog](https://github.com/gorakhargosh/watchdog) >=3.0.0 | 文件系统事件监听 |
| [aiohttp](https://github.com/aio-libs/aiohttp) >=3.9.0 | HTTP 服务器(控制 + 传输端点) |
| http.client (stdlib) | HTTP 客户端(asyncio.to_thread 异步) |
| [pystray](https://github.com/moses-palmer/pystray) >=0.19.5 | 系统托盘 |
| [Pillow](https://python-pillow.org/) >=10.0.0 | 托盘图标绘制 |
| tkinter / ttk (stdlib) | 配置 GUI |
| [tkinterdnd2](https://github.com/pmgagne/tkinterdnd2) >=0.3.0 | 拖拽支持(可选) |
| [ngrok](https://github.com/ngrok/ngrok-python) >=1.0.0 | ngrok 隧道 SDK(可选) |
| [PyInstaller](https://pyinstaller.org/) >=6.0.0 | exe 打包 |
## 开发
```bash
pip install -r requirements.txt
# 运行测试
pytest tests/ -v
# 开发运行
python main.py
# 多实例调试
python main.py --multi --port 39877 --config sync_config_2.json
```
### CLI 参数
| 参数 | 说明 |
|------|------|
| `--multi` | 跳过单实例检查(多实例调试用) |
| `--config PATH` | 指定配置文件路径 |
| `--port PORT` | 覆盖网络监听端口 |
## License
[MIT](LICENSE)