Files
SimpleRemoter/docs/FileTransferSpeed_Design.md
T
yuanyuanxianganddeepseek-v4-pro 59fead7b5f Fix: Deliver file-transfer speed via MasterSettings (was CMD_FILE_SPEED)
The file-transfer speed dialog previously sent a separate CMD_FILE_SPEED
(5 bytes) back-to-back before COMMAND_SCREEN_SPY / COMMAND_LIST_DRIVE when
opening remote desktop or file manager. The IOCP send path batches two
consecutive Send2Client calls into one WSASend of the accumulated
OutCompressedBuffer, so the client received the [FILE_SPEED][SCREEN_SPY]
command stream twice - the root cause of two remote-desktop dialogs popping
up on a single click (introduced in commit 76decec).

Fold the two values into MasterSettings (TransferChunkKb / TransferDurationMs,
uint16 each, before Reserved, sizeof unchanged at 1000) so they reach every
client on login and are broadcast to online clients when the dialog saves.
The client persists them into [transfer] on CMD_MASTERSETTING, guarded by
TransferChunkKb != 0 so older masters keep local defaults. CMD_FILE_SPEED(252)
and its client handler remain for backward compatibility, but the master no
longer sends it.

Update the design doc, the three GBK lang files' prompt entry, and the READMEs.

Co-Authored-By: deepseek-v4-pro
2026-10-07 21:19:04 +02:00

11 KiB
Raw Blame History

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

两个关键约束:

  1. 速度只在 InitFileUpload 时设定一次,库无运行时 setter(g_threadCount > 1 时提前返回,不会重设)。
  2. 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 构造,每会话一次) 读客户端配置(MasterSettings 登录下发写入),下次打开文件管理会话生效

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 下载方向(随 MasterSettings 登录下发到被控端)

下载速度不再用独立命令下发,而是折叠进 MasterSettings 结构体,随客户端每次登录成功第一时间下发(主连接建立即 SendMasterSettings),客户端持久化到 [transfer] 节后本地生效:

  1. common/commands.h:在 MasterSettings 结构体 Reserved 之前新增两个字段(sizeof 仍为 1000,Signature 偏移不变,向下兼容老客户端):
    uint16_t TransferChunkKb;      // 文件传输速度 chunkKb(随登录配置下发)
    uint16_t TransferDurationMs;   // 文件传输速度 durationMs(0=不限速)
    
  2. 主控端登录即下发:OnInitDialog() 读 THIS_CFG 的 [transfer](GetTransferSpeed,默认 64/50),填入 m_settings.TransferChunkKb/TransferDurationMs。登录链路本就把 m_settings 经 SendMasterSettings 下发给每个新连接客户端,无需任何额外命令。
  3. 主控端改速即时广播:OnFileTransferSpeed() 保存后同步更新 m_settings 两个字段并 SendMasterSettings(nullptr, m_settings),在线客户端即时收到新速度;新连接主机也自动获取。
  4. 被控端接收:客户端 CMD_MASTERSETTING 处理器(client/KernelManager.cpp:1116 附近)中,TransferChunkKb 非 0(老主控未携带时为 0,保留本地默认值)则把两数写入 iniFile(CLIENT_PATH) 的 [transfer] 节。
  5. 被控端调用点读配置:client/FileManager.cpp:40(CFileManager 构造)等 4 个 InitFileUpload 调用点均读 cfg.GetInt("transfer", ...),替换硬编码 64, 50。CFileManager 每会话重建(构造 Init、析构 Uninit),故下次打开文件管理即生效。

为何不用独立命令:v1.4.0 曾用独立 CMD_FILE_SPEED(252) 在 OnOnlineFileManager()/OnOnlineDesktopManager() 发 COMMAND_LIST_DRIVE/COMMAND_SCREEN_SPY 之前追加下发。主控端 IOCP 对两次背靠背 Send2Client 会合并为两次整包 WSASend(OutCompressedBuffer 累积),导致客户端收到重复的 [FILE_SPEED][SCREEN_SPY] 命令流——这正是“点击远程桌面弹出两个对话框”的根因(commit 76decec)。改为随 MasterSettings 登录下发后,下载方向不再有任何背靠背发送。 向后兼容:CMD_FILE_SPEED = 252 命令号与客户端 case CMD_FILE_SPEED 处理器仍保留,供老主控(仍按旧机制下发)与新客户端互通;新主控不再发送该命令。 关键:库用进程级引用计数,速度只在首次 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),由 MasterSettings 登录下发写入。

存 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 读配置(上传);OnInitDialog() 把 [transfer] 速度填入 m_settings;OnFileTransferSpeed() 保存后更新 m_settings 并经 SendMasterSettings(nullptr, ...) 广播
common/commands.h MasterSettings 新增 TransferChunkKb/TransferDurationMs(sizeof 不变,CMD_FILE_SPEED=252 保留向后兼容)
client/KernelManager.cpp CMD_MASTERSETTING 处理器写 [transfer](TransferChunkKb 非 0 才写)
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. 风险与备注

  1. Sleep 精度:Windows 计时器粒度约 15.6ms,durationMs 过小则失真(见 §3)。
  2. 实际速度 < 预估:预估为纯 chunk/delay 理论值,未计入 f() 网络发送耗时;对话框文案用“约/大概”表述。
  3. 结构体字段布局:TransferChunkKb/TransferDurationMs 加在 Reserved 之前(sizeof(MasterSettings) 仍为 1000,Signature 偏移不变),保证与 Go 侧/老客户端二进制兼容;TransferChunkKb == 0 作为“未下发”哨兵(老主控未填充 → 客户端保留本地默认值)。
  4. .rc 编码:2015Remote.rc 为 UTF-16LE(带 BOM),编辑必须按 Unicode 处理,避免破坏资源脚本。
  5. 跨平台:Linux/macOS 客户端下载路径暂不改;MasterSettings 新增字段位于原 Reserved 区(老/其他平台客户端不识别即忽略),向后兼容。
  6. 多语言 key 精确匹配:语言文件为 GBK 编码、key 不 trim(CIniParser 保留原始空格);译文 key 必须与源码逐字一致——源码为 UTF-8、经 /execution-charset:.936 编译为 GBK,·(U+00B7)、全角括号()等需一致,否则查不到翻译。

9. 实施顺序

  1. common/commands.h 扩 MasterSettings → resource.h 加 ID → .rc 加菜单项与对话框资源。
  2. 新建 CFileSpeedDlg.h → 2015RemoteDlg 接线(上传方向,可先验证)。
  3. 主控端经 MasterSettings 下发速度 → 客户端 CMD_MASTERSETTING 持久化 → 客户端调用点读配置(下载方向)。
  4. 编译验证(MSBuild 须传 -p:SolutionDir,见项目记忆)。