Add a novice-friendly "File Transfer Speed" dialog under the Advanced menu that controls file-transfer speed in both directions without touching SimplePlugins. Speed is passed through InitFileUpload's existing chunkSizeKb/sendDurationMs arguments, so the precompiled library stays untouched. Seven presets run from ~0.3 MB/s up to a new 10 MB/s tier plus an unlimited tier; the chosen value is stored in [transfer] ChunkKb/DurationMs. Upload reads THIS_CFG at the master's InitFileUpload (2015RemoteDlg.cpp:2225) and takes effect on restart. Download is pushed to the client over a new CMD_FILE_SPEED (252) command, sent from OnOnlineFileManager and OnOnlineDesktopManager before each session opens, and read by all four client InitFileUpload call sites via GetTransferSpeed so the process-global refcount cannot latch a stale speed. All Chinese strings go through LangManager, with en_US/zh_TW/de_DE entries added for the tier names, format strings and hint text. Co-Authored-By: deepseek-v4-pro
9.4 KiB
YAMA 文件传输速度调节设计
状态:已实现(Windows 端,含多语言)。其他平台(Linux/macOS 客户端)暂不实施。 读者:研发/评审人员。 关联文档:FILE_TRANSFER_V2.md(V2 协议)、Mcp_FileTransfer_Design.md(MCP 文件传输)。
1. 背景与目标
文件传输速度目前是写死的:SimplePlugins 库的 InitFileUpload(key, msg, hmac, chunkSizeKb, sendDurationMs, ...) 被 YAMA 所有调用点传 64, 50,折算 ≈ 1.25 MB/s。用户无法调节。
目标:在 YAMA 主菜单「高级」下新增一个面向小白的「文件传输速度」对话框——拖动滑块即完成设置,实时显示预估速度;不修改 SimplePlugins 库(库已编译,只通过其已暴露的 chunkSizeKb/sendDurationMs 两个入参控制)。
2. 速度原理(现状)
库内部把两个入参转成全局量(SimplePlugins/file_upload.cpp:221-222, 849-850):
static int chunk_size = 64 * 1024; // 默认 64KB
static int send_duration = 50; // 默认 50ms
// InitFileUpload: chunk_size = chunkSizeKb * 1024; send_duration = sendDurationMs;
发送循环每发一块就 Sleep(send_duration)(V1 FileBatchTransferWorker 与 V2 FileBatchTransferWorkerV2 共用这对全局量):
预估速度 = chunkSizeKb * 1024 / (sendDurationMs / 1000) 字节/秒
默认 64KB / 50ms ≈ 1.25 MB/s
两个关键约束:
- 速度只在
InitFileUpload时设定一次,库无运行时 setter(g_threadCount > 1时提前返回,不会重设)。 - V1 / V2 共用同一对全局量,一次设置同时覆盖
COMMAND_SEND_FILE(68)与COMMAND_SEND_FILE_V2(85)两种协议。
3. 档位表(权威唯一来源,主控端对话框维护)
| 档位 | 名称 | chunkKb | durationMs | 预估速度 |
|---|---|---|---|---|
| 0 | 最慢 | 64 | 200 | ≈ 0.3 MB/s |
| 1 | 慢 | 64 | 100 | ≈ 0.6 MB/s |
| 2 | 默认 | 64 | 50 | ≈ 1.25 MB/s |
| 3 | 快 | 128 | 50 | ≈ 2.5 MB/s |
| 4 | 更快 | 256 | 50 | ≈ 5 MB/s |
| 5 | 很快 | 512 | 50 | ≈ 10 MB/s |
| 6 | 不限速 | 1024 | 0 | 网络上限 |
- 滑块
CSliderCtrl范围0..6,SetPageSize(1),默认落在 2。 durationMs = 0表示不人为限速(Sleep(0)仅让出时间片,实际受带宽约束)。- 注意 Windows
Sleep粒度 ≈ 15.6ms:durationMs低于约 15ms 基本等同不限速。故档位要么 ≥ 20ms,要么直接 0ms,不设 5ms 之类的“名不副实”中间值。
预估文案:当前约 1.25 MB/s;durationMs==0 显示 不限速(受网络带宽约束)。
- 多语言:档位名(最慢/慢/默认/快/更快/很快/不限速)与预估格式串(
%s · 约 %.2f MB/s、%s · 不受限速(受网络带宽约束))经_TR/_LF走LangManager翻译表;语言文件以中文原文为 key。刻度标签拆成 最慢/默认/不限速 三个独立LTEXT以便各自翻译(英文宽度不同,不能靠空格对齐)。
4. 两个方向的生效链路
文件传输有两条方向,InitFileUpload 各自在不同进程/时机调用:
| 方向 | 发送方 | InitFileUpload 调用点 |
生效方式 |
|---|---|---|---|
| 上传(主控端 → 被控端) | 主控端 | server/2015Remote/2015RemoteDlg.cpp:2223(启动一次) |
读 THIS_CFG,重启主控端生效 |
| 下载(被控端 → 主控端) | 被控端 | client/FileManager.cpp:40(CFileManager 构造,每会话一次) |
读客户端配置,下次打开文件管理会话生效 |
4.1 上传方向(主控端本地,无协议改动)
2015RemoteDlg.cpp:2223 现为 InitFileUpload({},{},GetHMAC(),64,50,Logf,GetUpperHash()),改为读 THIS_CFG:
int chunkKb = THIS_CFG.GetInt("transfer", "ChunkKb", 64);
int durMs = THIS_CFG.GetInt("transfer", "DurationMs", 50);
InitFileUpload({}, {}, GetHMAC(), chunkKb, durMs, Logf, GetUpperHash());
4.2 下载方向(需下发到被控端)
新增命令 CMD_FILE_SPEED,照 CMD_QUALITY_LEVEL(78) 的“服务端下发 → 客户端持久化 → 本地生效”套路:
common/commands.h:新增CMD_FILE_SPEED = 252(命令号单字节,252 < 255 安全)。- 命令格式:
[cmd:1][chunkKb:uint16 LE][durationMs:uint16 LE],共 5 字节。- 传原始
chunkKb/durationMs而非档位索引,避免客户端复制映射表,映射表只存在于主控端对话框。
- 传原始
- 主控端下发:在
OnOnlineFileManager()(server/2015Remote/2015RemoteDlg.cpp:4737,发COMMAND_LIST_DRIVE开文件管理会话)发COMMAND_LIST_DRIVE之前,先经主连接把当前档位CMD_FILE_SPEED(5 字节)发给被控端;OnOnlineDesktopManager()(2015RemoteDlg.cpp:4723,发COMMAND_SCREEN_SPY)同理也发一次,覆盖“先开远程桌面再开文件管理”的引用计数时序。两处都在SendFileSpeedCommand()(:9310)统一读取并下发。 - 被控端接收:在客户端主连接分发
client/KernelManager.cpp:814的switch(COMMAND_LIST_DRIVE所在,case加在1280附近)加case CMD_FILE_SPEED,把两数写入iniFile cfg(CLIENT_PATH)的[transfer]节。 - 被控端调用点读配置:
client/FileManager.cpp:40(CFileManager 构造)改为读cfg.GetInt("transfer","ChunkKb",64)/DurationMs,替换硬编码64, 50。CFileManager 每会话重建(构造 Init、析构 Uninit),故下次打开文件管理即生效。
关键:库用进程级引用计数,速度只在首次
InitFileUpload时设定(g_threadCount > 1时提前返回,不重设)。客户端共 4 个InitFileUpload调用点(FileManager.cpp:40、ScreenManager.cpp:109、ScreenManager.cpp:873、KernelManager.cpp:1399),谁先初始化谁锁定速度。故这 4 处必须全部改为读同一配置,否则下载速度会被先初始化的管理器(如远程桌面会话)锁成旧值。
5. 配置存储
- 主控端:
THIS_CFG(ini)[transfer]节ChunkKb/DurationMs(int)。 - 被控端:
iniFile(CLIENT_PATH)的[transfer]节ChunkKb/DurationMs(int),由CMD_FILE_SPEED写入。
存 ChunkKb/DurationMs 两个 int 而非档位索引:自解释、直接对应 InitFileUpload 入参、不易错位。
6. 改动文件清单(Windows 端)
| 文件 | 改动 |
|---|---|
server/2015Remote/resource.h |
加 IDD_FILE_SPEED、ID_TOOL_FILE_SPEED、IDC_FSPEED_SLIDER、IDC_FSPEED_LABEL、IDC_FSPEED_RESET |
server/2015Remote/2015Remote.rc |
「高级」菜单加一项;新增对话框资源(滑块+预估标签+按钮)。文件为 UTF-16LE 编码,须按 Unicode 读写 |
server/2015Remote/CFileSpeedDlg.h |
新建:档位表 + OnHScroll 实时预估 + 恢复默认(照 CPreviewLevelsDlg.h 内联实现) |
server/2015Remote/2015RemoteDlg.h |
加 OnFileTransferSpeed() 声明 |
server/2015Remote/2015RemoteDlg.cpp |
消息映射 ON_COMMAND(ID_TOOL_FILE_SPEED, ...);2223 读配置(上传);OnFileTransferSpeed() 弹出对话框并写回 THIS_CFG;OnOnlineFileManager()/OnOnlineDesktopManager() 发 CMD_FILE_SPEED |
common/commands.h |
加 CMD_FILE_SPEED = 252 |
client/KernelManager.cpp |
case CMD_FILE_SPEED 写客户端配置(主连接 switch,814) |
client/FileManager.cpp、client/ScreenManager.cpp、client/KernelManager.cpp |
4 个 InitFileUpload 调用点读配置替换硬编码 64, 50(引用计数要求全部一致) |
server/2015Remote/lang/en_US.ini、zh_TW.ini、de_DE.ini |
追加「文件传输速度」相关 13 条中文原文→译文(GBK 编码,RCDATA "lang\*.ini" 由 rc.exe 嵌入) |
7. 生效时机与提示文案
- 上传速度:重启主控端生效。
- 下载速度:下次打开该主机的文件管理会话生效。
- 对话框保存后弹提示:
文件传输速度已保存。上传速度重启主控端后生效;下载速度下次打开文件管理或远程桌面后生效。
8. 风险与备注
Sleep精度:Windows 计时器粒度约 15.6ms,durationMs过小则失真(见 §3)。- 实际速度 < 预估:预估为纯
chunk/delay理论值,未计入f()网络发送耗时;对话框文案用“约/大概”表述。 - 命令号冲突:
CMD_FILE_SPEED=252需确认commands.h无重号;252 < 255(单字节uint8_t cmd上限)。 .rc编码:2015Remote.rc为 UTF-16LE(带 BOM),编辑必须按 Unicode 处理,避免破坏资源脚本。- 跨平台:Linux/macOS 客户端下载路径暂不改(
CMD_FILE_SPEED会被老/其他平台客户端静默忽略,向后兼容)。 - 多语言 key 精确匹配:语言文件为 GBK 编码、key 不 trim(
CIniParser保留原始空格);译文 key 必须与源码逐字一致——源码为 UTF-8、经/execution-charset:.936编译为 GBK,·(U+00B7)、全角括号()等需一致,否则查不到翻译。
9. 实施顺序
common/commands.h加命令号 →resource.h加 ID →.rc加菜单项与对话框资源。- 新建
CFileSpeedDlg.h→2015RemoteDlg接线(上传方向,可先验证)。 - 主控端下发
CMD_FILE_SPEED→ 客户端接收持久化 → 客户端调用点读配置(下载方向)。 - 编译验证(MSBuild 须传
-p:SolutionDir,见项目记忆)。