你的 App 在地铁里崩过吗?手撸一个 Android 弱网模拟工具

23 阅读9分钟

你的 App 在地铁里崩过吗?手撸一个 Android 弱网模拟工具

WeakNet 技术博客系列 | 第 1 篇


你的 App,真的准备好上线了吗?

做移动开发久了,一定遇到过这些场景:用户在地铁里打开你的 App,白屏了;进电梯之后,一个网络请求转了 30 秒才超时;连上商场公共 WiFi,DNS 被劫持到广告页,你的接口全挂了。QA 在办公室用着千兆 WiFi 测试一切正常,上线之后崩溃率飙升,用户投诉刷屏。

弱网问题是移动端最容易被忽视、又最容易翻车的一环。PC 上可以用 Charles、Clumsy、tc/NetEm 模拟弱网,但在 Android 真机上,能用的工具少之又少。要么依赖模拟器(Android Emulator Throttling),要么需要 root 权限(tc 命令),要么只能代理 HTTP 层(Charles/Fiddler),对 WebSocket、长连接、DNS、UDP 协议无能为力。

更关键的是,很多团队根本不测弱网。不是不想测,是没有好用的工具。结果就是:上线前没问题,上线后被用户教做人。如果你没有测试过这些场景,那只是还没被用户遇到。

这篇文章要介绍的项目叫 WeakNet,它是一个完全在 Android 用户态实现的弱网模拟工具。不需要 root,不需要代理配置,安装即用。它基于 Android 的 VpnService 拦截所有 IP 流量,在数据包层面进行延迟、丢包、限速、重复、乱序、篡改等操作,并且支持网络闪断DNS 故障模拟

WeakNet 能做什么

WeakNet 是一个 Android App(包名 com.awu.weaknet),它的核心能力是在数据包层面操纵所有 IP 流量

和 HTTP 代理方案不同,WeakNet 工作在 IP 层。它拦截的不是 HTTP 请求,而是所有 TCP、UDP、ICMP 数据包。这意味着无论你的 App 用的是 HTTP/HTTPS、WebSocket、QUIC、DNS 还是自定义 TCP 协议,全部都会被捕获和操纵。

它不需要 root 权限,利用 Android 系统提供的 VpnService API 创建一个虚拟网络接口(TUN),把系统的所有 IP 流量导入用户态处理。也不需要任何代理配置,App 安装后点击启动,系统会弹出一个 VPN 授权对话框,授权后即刻生效。

核心功能一览:

  • 12 个预设场景:覆盖 2G、3G、4G 弱信号、WiFi 弱、地铁、电梯、高延迟、丢包地狱、高铁、DNS 超时、数据校验等真实弱网环境
  • 5 种数据包操纵:限速(Throttle)、乱序(Reorder)、重复(Duplicate)、丢包(Loss)、篡改(Tamper)
  • 3 种延迟模型:均匀分布、高斯分布(Box-Muller 变换)、长尾分布(对数正态)
  • 2 种丢包模型:随机丢包(伯努利)、突发丢包(Gilbert 两状态马尔可夫链)
  • 网络闪断模拟:周期性断连/恢复,模拟地铁进隧道、进电梯等场景
  • DNS 故障注入:超时(静默丢弃)、失败(SERVFAIL)、劫持(伪造 A 记录)
  • 按 App 过滤:只对指定 App 生效,不影响系统和其他应用
  • 悬浮窗实时监控:显示当前上下行速率、丢包率、连接数等实时统计

12 个预设场景

WeakNet 内置了 12 个预设场景,覆盖了常见的弱网环境。每个预设都是根据真实场景调参的,开箱即用。

预设关键参数对应场景
无限制--基准对照
2G (EDGE)延迟 300ms + 丢包 5% + 限速 50kbps经典慢网
3G (HSPA)延迟 100ms + 丢包 2% + 限速 750kbps慢但可用
4G 弱信号延迟 50ms + 丢包 1% + 限速 3Mbps信号差的角落
WiFi 弱延迟 30ms + 限速 5Mbps + 抖动 15ms远离路由器
地铁延迟 200ms + 丢包 8% + 限速 1Mbps + 闪断 30s/2s进隧道周期断连
电梯延迟 500ms + 丢包 15% + 限速 100kbps + 闪断 20s/3s金属屏蔽频繁断网
高延迟延迟 2000ms + 抖动 500ms卫星网络级别
丢包地狱延迟 100ms + 丢包 30% + 重发 5% + 乱序极端环境
高铁延迟 150ms + 丢包 5% + 限速 800kbps + 闪断 60s/4s经过偏远地区
DNS 超时DNS 查询丢弃DNS 不可达
数据校验篡改 10%安全容错测试

其中"地铁"和"电梯"预设是实战中使用频率最高的两个。地铁预设模拟的是列车进隧道时周期性的信号中断:每 30 秒断连 2 秒,配合 200ms 延迟和 8% 丢包。电梯预设则更极端:金属屏蔽导致丢包率高达 15%,每 20 秒断连 3 秒。

当然,所有参数都支持完全自定义。延迟分布模型、丢包模型、限速方向(上传/下载/双向)、DNS 故障类型等都可以独立配置,保存为自定义 Profile 复用。

整体架构

在深入每一行代码之前,先从全局视角理解 WeakNet 的数据流。

App 流量 -> TUN 接口 (VpnService)
    -> PacketReader (阻塞读 TUN fd)
    -> PacketProcessor (解析 IP/TCP/UDP, TCP 状态机)
        -> ManipulationPipeline (Throttle -> Reorder -> Duplicate -> Loss -> Tamper)
        -> protect()'d Socket -> 真实网络

整个架构分为三层。

第一层是流量拦截。Android 的 VpnService 允许我们创建一个虚拟网络接口(TUN)。系统把所有匹配路由规则的 IP 流量写入这个 TUN 接口的文件描述符(FileDescriptor),我们在用户态通过阻塞读取这个 fd 就能拿到所有原始 IP 数据包。不需要 root,不需要 iptables,系统原生支持。

第二层是协议解析与代理PacketProcessor 是整个 App 最复杂的部分。它从 TUN 接口读到原始字节后,解析 IP 头、TCP 头或 UDP 头,提取出源地址、目标地址、端口号、TCP 标志位等信息。对于 TCP 流量,它维护了一个完整的用户态 TCP 状态机(TcpSession),在 App 和真实服务器之间充当透明代理。App 以为自己在和真实服务器通信,真实服务器以为自己在和 App 通信,实际上两边都在和 PacketProcessor 通信。

第三层是数据包操纵。解析后的数据包进入 ManipulationPipeline,这是一个由 5 个操纵器串联的管线。管线的设计借鉴了函数式编程中 flatMap 的思想:每个操纵器接收一个数据包,返回 0 到 N 个数据包(0 个就是丢包,2 个就是重复,1 个可能是被篡改过的)。管线顺序是 Throttle -> Reorder -> Duplicate -> Loss -> Tamper,这个顺序有讲究:先限速控制整体速率,再乱序和重复制造混乱,然后丢包筛选最终发出的包,最后对幸存的包进行篡改。

经过管线处理后,数据包通过 protect() 保护的真实 Socket 发送到实际网络。protect() 是 Android VpnService 的关键 API,它告诉系统"这个 Socket 的流量不要走 VPN,直接走真实网络"。如果忘记调用 protect(),数据包会再次被路由到 TUN 接口,形成无限循环。

核心组件

组件职责
WeakNetVpnServiceVpnService 实现,建立 TUN 接口,管理生命周期
VpnThread编排器,协调 TUN 读写、UDP 轮询、统计更新、会话清理
PacketProcessor核心:IP/TCP/UDP 解析、TCP 状态机、DNS 拦截、闪断守卫
ManipulationPipeline串联 5 个操纵器,用 flatMap 实现 1->N 的包变换
DisconnectScheduler闪断计时器,周期性切换连接/断开状态
DnsResponderDNS 响应构造器,支持 SERVFAIL 和 IP 劫持两种故障模式

其中 PacketProcessor 承担了最多的职责。它不仅是协议解析器,还是 TCP 状态机的管理者、DNS 拦截的入口、闪断状态的守卫。后续第 3 篇和第 4 篇会分别深入数据包解析和 TCP 代理的实现细节。

VpnThread 是整个系统的编排器。它在 VPN 线程上阻塞运行 PacketReader(持续读取 TUN fd),同时启动多个协程:UDP 上游轮询(每 15ms)、统计更新(每 500ms)、会话清理(每 10s)以及闪断调度(每 50ms)。

DisconnectScheduler 使用取模周期调度实现网络闪断。它以 50ms 精度被协程驱动,在断连期间所有新建连接被拒绝,所有数据包被丢弃;在恢复连接时,所有存活的 TCP 会话被 RST 重置,强制 App 重新建连。

系列导航

这是 WeakNet 技术博客系列的第 1 篇。整个系列共 8 篇,从零到一完整拆解"如何在用户态实现一个弱网模拟工具"。每篇聚焦一个技术主题,可独立阅读,系列串联即完整项目。

篇数主题一句话描述
第 2 篇Android VpnService 详解:如何把所有流量导入用户态双路由策略、protect() 避坑
第 3 篇手写 IP/TCP/UDP 解析器:一次搞懂网络包结构从 raw bytes 到结构化数据
第 4 篇在 Android 用户态实现 TCP 代理:从 SYN 到 FIN 的完整生命周期TCP 状态机与 pre-ACK 丢包设计
第 5 篇限速/丢包/乱序/重复/篡改:弱网模拟的 5 把利刃flatMap 式管线架构
第 6 篇别只会用均匀分布:三种延迟模型和两种丢包模型的原理与实现Box-Muller、Gilbert、对数正态分布
第 7 篇网络闪断 + DNS 故障:弱网模拟中最容易被忽视的两个场景取模周期调度、DNS 协议构造
第 8 篇踩过的 8 个坑:Android 用户态网络编程避坑指南线程池、同步机制、踩坑实录

写在最后

弱网测试不是可选项。你的用户不会永远在千兆 WiFi 下使用你的 App。地铁、电梯、高铁、地下室、信号死角——这些才是真实的网络环境。如果你没有在发布前测试过这些场景,那只是在赌运气。

WeakNet 的目标是让弱网测试变得简单:安装 App,选一个预设,点启动,你的手机立刻变成一个弱网环境。不需要 root,不需要代理配置,不需要连电脑。

下一篇,我们将从 VpnService 入手,详细讲解如何把系统的所有流量导入用户态。


项目地址github.com/baithinking…