问题现象
使用触摸屏持续拖动时,偶尔会出现严重卡顿。
正常情况下,客户端每秒会发送约 150 到 250 个 UDP 控制包;卡顿时,Windows 抓包发现 47999 端口的总包数会突然降到约 18 到 34 包每秒。此时触摸移动明显滞后。
排查结果
客户端触摸采样、Java 到 JNI 的队列、原生触摸发送逻辑均正常。
卡顿期间仍能稳定生成和提交约 230 到 240 个触摸 MOVE 每秒,但实际 UDP 输出仅剩约 20 到 30 包每秒。
进一步定位到 ENet 对不可靠包的节流逻辑。触摸 MOVE 使用不可靠包发送,ENet 在以下条件下会降低 packetThrottle:
rtt > lastRoundTripTime + 2 * lastRoundTripTimeVariance
一次实际卡顿日志中的数据:
基线 RTT:23ms
RTT 方差:1ms
触发阈值:25ms
实际 RTT:26 到 31ms
RTT 仅短暂增加 3 到 8ms,就连续触发降速:
32 -> 30 -> 28 -> ... -> 0
packetThrottle = 32 时不丢不可靠包;packetThrottle = 0 时全部不可靠包都会被丢弃。因此持续触摸 MOVE 被全部节流,只剩控制、ACK、ping 等必要 UDP 包,最终表现为触摸卡顿。
当 RTT 回落后,节流会逐步恢复,UDP 包率也会从约 20 包每秒恢复到 200 包每秒以上。
卡顿期间可靠包丢失率仅约 0.17% 到 0.25%,更像是局域网短暂 RTT 抖动被过度判定为拥塞,而不是持续丢包或 Android 输入队列堵塞。
最小改动建议
修改文件:
app/src/main/jni/moonlight-core/moonlight-common-c/enet/peer.c
原始代码约第 79 行:
if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance)
建议替换为:
if (rtt > peer -> lastRoundTripTime +
((2 * peer -> lastRoundTripTimeVariance > 10) ?
2 * peer -> lastRoundTripTimeVariance : 10))
该修改只为 RTT 节流判断增加 10ms 的最小容忍窗口,不改变可靠包处理、触摸事件生成、触摸 MOVE 的不可靠发送方式,或 ENet 的其它节流恢复逻辑。
子模块说明
本问题的最小修改位于 ENet 子模块中,不直接属于 Moonlight Android 主仓库。
当前依赖关系为:
moonlight-android
└─ moonlight-common-c
└─ enet
└─ peer.c
因此我无法直接提交一个可正常检出的 Moonlight Android PR:主仓库只能记录子模块指向的提交,不能包含 enet/peer.c 的源码差异。如果该 ENet 提交尚未存在于上游仓库,其他开发者或 CI 初始化子模块时无法获取该版本。
想请作者评估更合适的处理方式:
- 在 Moonlight 项目中对触摸 MOVE 单独处理或配置更宽松的 ENet 节流策略;
- 向 ENet 子模块上游提交通用修复,再逐层更新
moonlight-common-c 和 Moonlight Android 的子模块版本;
- 采用其他能避免短暂 RTT 抖动导致触摸 MOVE 被完全节流的方案。
复现方式
问题为偶发,更容易在持续快速拖动触摸屏时观察到。卡顿发生时,可在 Windows 侧抓取 47999 UDP 端口流量,看到包率从约 150 到 250 包每秒下降至约 20 包每秒,而客户端仍持续产生正常数量的触摸事件。
问题现象
使用触摸屏持续拖动时,偶尔会出现严重卡顿。
正常情况下,客户端每秒会发送约 150 到 250 个 UDP 控制包;卡顿时,Windows 抓包发现 47999 端口的总包数会突然降到约 18 到 34 包每秒。此时触摸移动明显滞后。
排查结果
客户端触摸采样、Java 到 JNI 的队列、原生触摸发送逻辑均正常。
卡顿期间仍能稳定生成和提交约 230 到 240 个触摸 MOVE 每秒,但实际 UDP 输出仅剩约 20 到 30 包每秒。
进一步定位到 ENet 对不可靠包的节流逻辑。触摸 MOVE 使用不可靠包发送,ENet 在以下条件下会降低
packetThrottle:一次实际卡顿日志中的数据:
RTT 仅短暂增加 3 到 8ms,就连续触发降速:
packetThrottle = 32时不丢不可靠包;packetThrottle = 0时全部不可靠包都会被丢弃。因此持续触摸 MOVE 被全部节流,只剩控制、ACK、ping 等必要 UDP 包,最终表现为触摸卡顿。当 RTT 回落后,节流会逐步恢复,UDP 包率也会从约 20 包每秒恢复到 200 包每秒以上。
卡顿期间可靠包丢失率仅约
0.17% 到 0.25%,更像是局域网短暂 RTT 抖动被过度判定为拥塞,而不是持续丢包或 Android 输入队列堵塞。最小改动建议
修改文件:
原始代码约第 79 行:
建议替换为:
该修改只为 RTT 节流判断增加
10ms的最小容忍窗口,不改变可靠包处理、触摸事件生成、触摸 MOVE 的不可靠发送方式,或 ENet 的其它节流恢复逻辑。子模块说明
本问题的最小修改位于 ENet 子模块中,不直接属于 Moonlight Android 主仓库。
当前依赖关系为:
因此我无法直接提交一个可正常检出的 Moonlight Android PR:主仓库只能记录子模块指向的提交,不能包含
enet/peer.c的源码差异。如果该 ENet 提交尚未存在于上游仓库,其他开发者或 CI 初始化子模块时无法获取该版本。想请作者评估更合适的处理方式:
moonlight-common-c和 Moonlight Android 的子模块版本;复现方式
问题为偶发,更容易在持续快速拖动触摸屏时观察到。卡顿发生时,可在 Windows 侧抓取 47999 UDP 端口流量,看到包率从约 150 到 250 包每秒下降至约 20 包每秒,而客户端仍持续产生正常数量的触摸事件。