用C/C++ Socket模拟网络条件

15
我正在寻找一种将网络仿真添加到套接字中的方法。
基本解决方案是通过某种方式为连接添加带宽限制。
对我来说理想的解决方案应该具备以下特点:
- 支持高级网络属性(延迟、丢包) - 开源 - 具有与标准套接字类似的 API(或者封装在它们周围) - 在 Windows 和 Linux 上都能工作 - 支持 IPv4 和 IPv6
我看到了一些在系统级别上工作的选项,甚至可以作为代理(Dummynet、WANem、neten 等),但这对我来说行不通,因为我想手动模拟每个套接字(例如,用调制解调器仿真打开一个套接字,用 3G 仿真打开另一个套接字。基本上我想知道这些工具是如何实现的。
编辑:我需要将此功能嵌入到自己的产品中,因此使用额外的盒子或需要手动配置的第三方工具是不可接受的。我想编写代码,做和这些工具一样的事情,我的问题是如何做到这一点。
结语:回想起来,我的问题有点误导人。显然,没有直接在套接字上实现我想要的功能的方法。有两个选择:
通过在发送和接收操作之前添加延迟,可以获得非常粗略的网络仿真(固定延迟用于延迟,延迟发送,以便不要发送超过每秒 X 个字节,用于带宽)。
将网络仿真逻辑作为代理添加(基于 @m0she 和其他人的答案)。可以通过代理发送请求,或使用代理拦截请求,然后添加所需的模拟。但是,与其编写自己的代理实现,使用一个现成的解决方案更为明智 - 从我所见到的情况来看,Dummynet 可能是最佳选择(这就是 webpagetest.org 所使用的内容)。其他选项可以在下面的答案中找到,我还会添加 DonsProxy
这是更好的方法,因此我接受了这个答案。

1
这是一个C++应用程序,虽然你说得对 - 套接字是C语言的(winsock和POSIX)。 - Yasei No Umi
1
C语言本身并不具备socket功能...需要使用操作系统提供的库(带有C接口)。这并非吹毛求疵 :) - Drew Hall
@DrewHall - 这不是吹毛求疵,这是有用的。基本上这意味着我无法使用标准套接字库真正模拟不同的网络条件。那么还有什么其他的选择? - Yasei No Umi
你想要给真实的网络流量增加延迟,还是仅模拟网络流量? - Paul Coccoli
@PaulCoccoli - 我正在生成我需要的特定流量(充当客户端,向服务器发出请求),我希望服务器“感觉”自己正在处理一个连接缓慢的客户端。 - Yasei No Umi
8个回答

6
您可以在您的软件中编译一个代理来实现这个功能。它可以是完整的socks代理实现(例如这个),或者更好的是,只服务于您的目的、不需要添加与目标通信的前缀和其他socks开销的简单内容。
该代码可以作为独立进程或线程在您的进程中运行。
向代理添加限速不应该太难。您可以:
  • 如果数据通过了带宽限制,则延迟其转发。
  • 通过在缓冲区的读写操作前添加计时器,增加延迟。
  • 如果您正在使用基于连接的协议(如TCP),那么丢弃数据包是没有意义的,但是对于基于数据报的协议(UDP)也很容易实现。
连接创建API与正常的posix/winsock有些不同(除非您使用一些宏或其他魔法),但其他所有内容(发送/接收/选择/关闭等)都是相同的。

这是一个有趣的想法,但是如果我必须编写延迟代码,我可以直接在发送/接收数据的代码中编写它,而不需要代理(请参见@Paul Coccoli的答案)。 - Yasei No Umi
@yasei-no-umi - 这就是我打算的。 我认为代理解决方案更简单易行,更适合那些熟悉winsock / posix socket API的人,并且更加解耦(例如会让你更改实现为外部进程或第三方解决方案,这将提供更复杂的限流功能)。 - m0she
不必从头编写代理,我可以使用具有此功能的代理(如Dummynet)-按所需网络动态创建管道,并通过其发送流量。 - Yasei No Umi

6
如果您正在将此功能集成到产品中,则应在套接字API上实现一层抽象,以便可以在运行时选择自己的实现。 或者,您可以实现每个套接字函数的包装器,并选择调用自己的版本还是系统的版本。
至于添加延迟,您可以让套接字API的实现启动一个线程。 在该线程中,有一个按时间排序的优先队列(即这个后台线程执行非常基本的离散事件模拟)。 每个您发送或接收的“数据包”都可以与交付时间一起排队。 每个交付时间都应添加一定量的延迟。 我建议使用带有高斯分布的某种随机数生成器。
后台线程还必须模拟连接的另一侧,不过听起来您可能已经实现了那部分?

这是我需要的最接近的内容,也是我一开始想到的。我希望能得到一些关于框架/代码片段的帮助,使其更容易和准确,但看起来我需要自己编写。至于另一侧,我想在接收端实现相同的想法(即在接收下一个缓冲区之前添加延迟) - 我不想对另一侧引入任何变化 - 这是我想要按原样测试的服务器。整个事情的目的是让服务器相信它正在与一个慢速客户端交互。 - Yasei No Umi

2

谢谢,但不是我要找的 - 它在系统级别上,我正在寻找更低级别的(Socket)。此外,所谓的多平台实际上是指Windows和Linux,但不包括Mac。 - Yasei No Umi

2
这个答案可能对你在使用Linux时有所帮助:模拟Linux上的延迟和丢包。它涉及到一个名为netem的内核模块,可以模拟各种网络问题。
如果你想处理TCP连接,由于很多错误处理(如恢复丢失的数据包)都是在内核中完成的,因此“数据包丢失”可能会有问题。以跨平台方式模拟这个过程可能很困难。

数据包丢失对我来说不是必须的,但编程实现是必须的 - 我无法安装或设置netem。 - Yasei No Umi

1

通常情况下,您会向网络添加一个网络设备来限制带宽或延迟,基于端口的方式,然后您只需连接到分配给特定类型的糟糕网络测试的端口,无需进行代码更改或修改即可实现所需效果。

最简单的方法是将iptables规则添加到充当代理的Linux服务器上。

如果您想在没有单独设备的情况下使用它,请尝试使用trickle,这是一种可以在客户端PC上限制您的网络速度的软件包。(或适用于Windows)


我希望这个功能成为我的产品的一部分,因此我不能安装/配置另一个产品。基本上,我想知道这些人是如何做到的? - Yasei No Umi

1
您可能想要查看WANem http://wanem.sourceforge.net/。WANem是开源软件,根据GNU通用公共许可证授权。
WANem允许应用程序开发团队设置透明的应用程序网关,可用于模拟WAN特性,如网络延迟、数据包丢失、数据包损坏、断开连接、数据包重新排序、抖动等。

我看过WANem(我甚至在我的问题中提到了它),但这不是我要找的。 - Yasei No Umi

1

如果您想要一个仅由您控制的软件解决方案,那么您将不得不自己实现它。我不知道是否存在这样的现成软件包。

虽然在套接字上添加包装层可以让您能够引入延迟,但这并不足以引入丢失或乱序传递。为了模拟这些活动,您实际上需要拦截两个TCP堆栈之间传输的数据。

我建议的方法是使用一个隧道设备(例如tunX)。路由应设置为客户端认为到服务器的路径是通过tunX。额外的代码(可能在不同的线程中运行)会在tunX上俯视地拦截流量,并执行您的增强行为,然后将数据包转发到真正的物理接口,以便将流量发送到您的服务器。对于从物理接口到达的来自服务器的数据包,情况将相反。这些数据包将被客户端代码拦截,行为增强,然后通过tunX转发。

然而,既然您正在测试客户端软件,我不清楚为什么您要将此代码嵌入到发布的软件中,除非该软件本身是一款模拟WAN的客户端。


1

我认为您可以使用类似网络模拟器的工具。它是免费的,适用于Windows系统。

唯一需要做的事情就是设置您的程序以使用正确的端口(当然还有网络设置)。


谢谢,但这对我不起作用。根据他们的常见问题解答 - “目前每个网络接口卡最多可以创建32个流。仅当网络模拟器的GUI组件正在运行时,流才处于活动状态。” 我需要更多的流,并且需要通过编程设置(而不是通过UI),还需要在Linux上使用。 - Yasei No Umi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接