弱网测试工具 Clumsy
弱网测试工具 Clumsy
来源 https://www.jianshu.com/p/13321b39d513
clumsy 能在 Windows 平台下人工造成不稳定的网络状况,方便你调试应用程序在极端网络状况下的表现。
简介
利用封装 Winodws Filtering Platform 的WinDivert 库, clumsy 能实时的将系统接收和发出的网络数据包拦截下来,人工的造成延迟,掉包和篡改操作后再进行发送。无论你是要重现网络异常造成的程序错误,还是评估你的应用程序在不良网络状况下的表现,clumsy 都能让你在不需要额外添加代码的情况下,在系统层次帮你达到想要的效果:
特色:
- 下载即用,不需要安装任何东西。
- 不需要额外设置,不需要修改你的程序的代码。
- 系统级别的网络控制,可以适用于命令行,图形界面等任何 Windows 应用程序。
- 不仅仅只支持 HTTP,任何 TCP, UDP 的网络连接都可以被处理。
- 支持本地调试(服务器和客户端都在 localhost)
- "热插拔",你的程序可以一直运行,而 clumsy 可以随时开启和关闭。
- 实时调节各种参数,详细控制网络情况。
实例
下面的动画展示了 clumsy 作用于一个本地的基于 netcat 的 UDP 服务器/客户端的情况。仔细观察你可以看到数据根据在 clumsy 的影响下产生了相应的变化。 如果你基本知道了 clumsy 是干什么用的,不妨到下载页面选择适用于你系统的版本进行下载。
详细信息
clumsy 首先根据用户选择的 filter 来拦截指定的网络数据。在 filter 中可以设定你感兴趣的协议(tcp/udp),端口号,是接收还是发出的端口。你也可以通过简单的逻辑语句来进一步缩小范围。当 clumsy 被激活时,只有符合这些标准的网络数据会被进行处理,而你不感兴趣的数据仍然会由系统正常传输。
当被 filter 的网络数据包被拦截后,你可以选择 clumsy 提供的功能来有目的性的调整网络情况:
- 延迟(Lag),把数据包缓存一段时间后再发出,这样能够模拟网络延迟的状况。
- 掉包(Drop),随机丢弃一些数据。
- 节流(Throttle),把一小段时间内的数据拦截下来后再在之后的同一时间一同发出去。
- 重发(Duplicate),随机复制一些数据并与其本身一同发送。
- 乱序(Out of order),打乱数据包发送的顺序。
- 篡改(Tamper),随机修改小部分的包裹内容。
尽管当前宽带网络连接十分普及,但网络传输其本身在本质上总不是稳定的。如果你的应用程序中没有应对各种情况的处理,那么有可能一个丢失的 UDP 包裹都会让你的程序崩溃。正确的调试这类行为 显然需要再代码结构上进行仔细的设计和处理,还会很花功夫。而且在某些封装紧密的开发环境(Unity3D 自带的网络库可能是一个例子)下会更麻烦。clumsy 以尽可能减轻程序员负担为目标, 希望提供一个简单方便(但并不完美)的解决方案。
项目的代码可以在github上获取。在下载页面有编译好的版本。强烈建议在使用前花点时间阅读一下文档,来 了解 clumsy 的功能和限制。
文档
术语
为了后文阅读方便,这里列出本页面中反复使用的一些术语。
- 回送数据包(Loopback packets): 从本机 (可以简单理解为 127.0.0.1) 发送到本机的数据包。通常在调试程序的时候为了配置简单,很多情况下就是简单的把服务器和客户端都架设在一台机器上,回送数据包在这种情况下就很常见。
- 输入数据包(Inbound packets): 当前计算机接收到的数据包。其来源可能是网络上的其他机器,也可能就是本机。
- 输出数据包(Outbound packets): 从当前计算机发送出去的数据包。
- 数据包过滤(Filtering): 大部分情况你只对所有的数据包的一个子集感兴趣。在 clumsy 里你可以提供一个 filter 来指定你感兴趣的数据包发送至/发送于某个 IP,包裹协议,以及一些其他的标准。
- 数据包捕获(Capturing packets): 根据之前设定的 filter, clumsy 会拦截下这些符合标准的数据包。在你的程序发出数据包之后,或者再接受到数据包之前,clumsy 会拦截下这些包裹进行处理,进行指定的处理来达到模拟劣化网络环境的效果。
- 重新注入数据包(Reinjecting packets): 在数据包被拦截下来后,他们还是需要被重新发送给目标的端口,这样应用程序才能接收到它们并继续工作。这个步骤科学的讲就叫做重新注入 (reinjecting) 数据包。
限制
!这部分内容非常重要,请务必不要略过。
目前的实现中有一些难以绕过的限制和问题,列表如下:
- 回送的输入数据包(Loopback inbound packets)无法被重新注入。
仔细想想你就会发现我们没有很好的方法来区分一个回送数据包到底是被发出还是被接收到,因为它们的目标和来源 IP 地址都是本机。事实上 clumsy 底层的 WinDivert,以及其基于的 Windows Filtering Platform 把所有的回送数据包统统认为是输出 (Outbound) 数据包。这里需要记住的是,当你在本机上处理回送数据包的时候,你不能把 "inbound" 设置在 filter 条件中,最简单的方法就是在条件中简单的加上 "outbound"。另一件容易出问题的事情是,你本机的 IP 不仅仅只有 127.0.0.1 一个,还有类似路由器分配给你机器的 IP 也是属于你的本机 IP。 - 回送数据包会被处理两次。
因为所有的回送数据包都被认为是输出数据包,clumsy 会重复处理它们两次。一次是在发出的时候,一次是在接受到的时候。一个简单的例子是简单的把 filter 设置为 outbound,然后在 clumsy 中设置 500ms 的延迟并开启,之后在命令行里 ping localhost。这时你会发现延迟是 1000ms。当然你可以仔细的设置 filter 条件通过设置端口来只捕捉一部分的回送数据包,但是这样会比较麻烦。最简单的做法就是记住这个问题,然后设置参数的时候做相应的计算。 - 输入数据包的重新注入有些问题。
根据上面的描述,回送的输入包裹无法被重新注入。问题是现在有些来自包裹虽然其地址不是本机的 IP,其偶尔也会被认为是输入包裹。这种情况如果被捕捉到是无法进行重新注入的。这个问题仅仅影响非回送数据包,所以如果你仅仅是在本机上调试服务器和客户端那么是不会有这个问题的。未来版本的目标是准确的重现这个问题并进行修复。 - 无法根据进程来进行数据包过滤。
全系统级的数据包捕获虽然被列在了功能里,实际上是在当前的实现下,没法找到一个合适且稳定的方法来进行根据进程的数据包捕获。
如何使用
首先请根据你系统的版本(32位或64位)下载 clumsy 最新版本。注意如果你安装的是64位的系统那么一定要下载64位的 clumsy。另一件重要的事实 clumsy 需要管理员权限才能正常工作。双击打开 clumsy 的话会弹出 UAC 对话框。如果没有的话请右键点击 clumsy.exe 选择"以管理员身份运行"。如果一切正常,你应该能看到如下图的界面:
根据上图中的数字图标顺序:
- Filter。详细的 filter 语法在下一部分有仔细的讲解。但是简单的讲它跟你常见的编程语言中的 if 中的条件写法几乎一样。数据包将根据这个标准被拦截。
- 预设 Filte。 这里提供了一系列内置的预设 Filter 供你使用。你可以通过参考他们来写你自己需要的 Filter,也可以把你发现常用的 filter 写到 clumsy.exe 所在文件夹下的 config.txt 中。
- 开始/结束 按钮。点击此按钮 clumsy 就会开始捕捉数据包。在某些情况下,比如提供的 filter 语法有问题 clumsy 无法正常开始工作。请参考底部的提示进行操作。如果一切正常按钮上的文字会变为 "Stop",按下就会停止捕捉包裹,系统网络会恢复正常。按钮左边有一个小图标,每当数据包被捕获的时候它会变绿,当包裹重新注入失败的时候它会变红色。这时你应该考虑是否遇到了上面限制中提到的问题。另外需要提到的是开启和结束可以随时进行并实时生效。
- 功能控制。勾选功能对应的选框来开启对应的功能。每个功能左边也有一个小图标,其起作用的时候就会变绿。在数据包捕获开启的状态下,你可以实时开启/关闭任何功能,它们都会即时生效。
- 参数控制。对每个功能,都附有对应的参数控制控件让你进行详细的配置。其中最常见的有:
- Inbound/Outbound: 是否处理输入/输出数据包。这里在 filter 的控制之外额外提供一个选择的机会,并且也可以在实时生效。
- Chance: 功能生效的概率。显然你需要把它们设置在一个合理的范围内才不会让网络在可以接受的范围内劣化。
- 状态。显示 clumsy 当前状态的帮助信息。
clumsy 提供的功能应该很容易理解,你可以在首页的介绍中获得简单的描述,或者你也可以直接试试看。如果你一下子没想起来有什么地方可以用的上,你可以把 filter 设置为 "inbound and outbound" 然后打开你最喜欢的浏览器访问任意网站,看看 clumsy 效果如何。.
Filter 语法
这里 filter 中提供的语句会被直接作为参数提供给 WinDivert。语法在 WinDivert 的文档中有详细的描述。如果你写过一点程序你会发现这个语法跟你放在 if 里面的判断表达式非常类似。你可以用 and, or, not 和 括号 来表达简单的逻辑规则。类似 =, !=, ‹, › 的操作符也可以被使用。下面是 filter 中可以使用的变量的列表(直接拷贝自 WinDivert 的文档)。你也可以通过以预设的 filter 作为例子参考。
outbound | 是否为输出数据包 |
inbound | 是否为输入数据包 |
ifIdx | 网络设备 index |
subIfIdx | 副网络设备 index |
ip | 是否为 IPv4 |
ipv6 | 是否为 IPv6 |
icmp | 是否为 ICMP |
icmpv6 | 是否为 ICMPv6 |
tcp | 是否为 TCP |
udp | 是否为 UDP |
ip.* | IPv4 的参数 (见 DIVERT_IPHDR) |
ipv6.* | IPv6 的参数 (见 DIVERT_IPV6HDR) |
icmp.* | ICMP 的参数 (见 DIVERT_ICMPHDR) |
icmpv6.* | ICMPV6 的参数 (见 DIVERT_ICMPV6HDR) |
tcp.* | TCP 的参数 (见 DIVERT_TCPHDR) |
tcp.PayloadLength | TCP 数据段长度 |
udp.* | UDP 的参数 (见 DIVERT_UDPHDR) |
udp.PayloadLength | UDP 数据段长度 |
常见问题
无法开始,错误代码 "3"
在命令行中输入以下两行并执行:
sc stop WinDivert1.0 sc delete WinDivert1.0
在下一版本中这个问题应该会被修正。
clumsy 开始运行后使得网络变慢。
当你输入了 filter 点击 '开始' 后,clumsy 就已经开始截获包裹。这个一定会有一定的效率损失。但好消息是 clumsy 本身就是为了模拟网络状况糟糕的软件所以这个问题不是很严重。 另一方面,你应该精确的设置 filter 的内容,让它尽可能准确的捕捉你感兴趣的数据包。
延迟比在 Lag 里设置的要严重很多。
这个问题的重点在于,clumsy 里面的设置并不是准确的可以用来做测速的值。其作用更倾向于作为参数控制软件的行为。 另一个需要理解的是,这里的延迟是作用于每一个网络包裹的。比如建立一个 TCP 连接需要至少 3 个包裹。那么如果 clumsy 引入了 20ms 的延迟,对于每一个 TCP 连接则至少引入了 3*20=60ms 的延迟。如果是要载入一个网页的话,有部分 HTTP 请求必须是要按顺序完成后页面才能开始渲染。假如要完成 1, 2, 3 这三个请求,那么 clumsy 在这里至少引入了 3*60=180ms 的延迟。 所以说如果 clumsy 让网络变的很慢的话也不用太担心。
在 Windows Server 2008 上报错。
安装这个系统补丁可以解决这个问题。 参见这个 issue。
=============== End