本文是 NS3 学习笔记的第三章节,主要是对 FatTree 的实现
📊 扩展阅读:为了更直观地理解 FatTree 的拓扑结构,你可以查看 FatTree 网络架构分析 页面。本文的代码可以在Fat-Tree NS3代码实现获取
ns-3 Fat-Tree 数据中心网络仿真代码详解
项目概述
仿真目标
本项目使用 ns-3.44 网络仿真器实现了一个 k=4 的 Fat-Tree 数据中心网络拓扑,并通过 ECMP (等价多路径) 路由实现负载均衡。
拓扑规模
1
2
3
4
5
| 总节点数: 36
├── 服务器: 16 台 (4 个 Pod × 4 台/Pod)
├── 接入交换机: 8 个 (4 个 Pod × 2 个/Pod)
├── 汇聚交换机: 8 个 (4 个 Pod × 2 个/Pod)
└── 核心交换机: 4 个
|
拓扑结构图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| 核心层 (Core Layer)
┌────┬────┬────┬────┐
│ C0 │ C1 │ C2 │ C3 │
└─┬──┴─┬──┴─┬──┴─┬──┘
│ │ │ │
┌─────────────┴────┴────┴────┴─────────────┐
│ │
汇聚层 (Aggregation Layer) │
┌────┬────┐ ┌────┬────┐ ┌────┬────┐ ┌────┬────┐
│ A6 │ A7 │ │ A6 │ A7 │ │ A6 │ A7 │ │ A6 │ A7 │
└─┬──┴─┬──┘ └─┬──┴─┬──┘ └─┬──┴─┬──┘ └─┬──┴─┬──┘
│ │ │ │ │ │ │ │
接入层 (Access Layer)
┌─┴──┬─┴──┐ ┌─┴──┬─┴──┐ ┌─┴──┬─┴──┐ ┌─┴──┬─┴──┐
│ S4 │ S5 │ │ S4 │ S5 │ │ S4 │ S5 │ │ S4 │ S5 │
└┬─┬─┴┬─┬─┘ └┬─┬─┴┬─┬─┘ └┬─┬─┴┬─┬─┘ └┬─┬─┴┬─┬─┘
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
服务器层 (Server Layer)
┌┴┐┌┴┐┌┴┐┌┴┐ ┌┴┐┌┴┐┌┴┐┌┴┐ ┌┴┐┌┴┐┌┴┐┌┴┐ ┌┴┐┌┴┐┌┴┐┌┴┐
│0││1││2││3│ │0││1││2││3│ │0││1││2││3│ │0││1││2││3│
└─┘└─┘└─┘└─┘ └─┘└─┘└─┘└─┘ └─┘└─┘└─┘└─┘ └─┘└─┘└─┘└─┘
Pod 0 Pod 1 Pod 2 Pod 3
|
仿真结果验证
FlowMonitor 统计数据分析
运行仿真后生成的 DCN_FatTree_FlowStat.flowmon 文件显示了 4 个流的完整统计信息:
Flow 1: TCP 数据流 (Pod3 → Pod2)
1
2
3
4
| 源地址: 10.3.0.1 (Pod3.Server0)
目标地址: 10.2.0.1 (Pod2.Server0)
协议: TCP (protocol="6")
端口: 49153 → 80
|
性能指标:
- ✅ 发送数据: 1,097,192 字节 (~1.05 MB)
- ✅ 接收数据: 1,097,192 字节 (~1.05 MB)
- ✅ 丢包率: 0% (lostPackets=”0”)
- ✅ 发送包数: 1,869 个
- ✅ 接收包数: 1,869 个
- ✅ 平均延迟: 27.2 µs (50,841,900 ns / 1,869)
- ✅ 最小延迟: 770 ns
- ✅ 最大延迟: 48,358 ns
结论: TCP 流量成功传输完整的 1 MB 数据,无丢包,延迟稳定在微秒级别。
Flow 2: TCP ACK 流 (Pod2 → Pod3)
1
2
3
4
| 源地址: 10.2.0.1 (Pod2.Server0)
目标地址: 10.3.0.1 (Pod3.Server0)
协议: TCP (protocol="6")
端口: 80 → 49153
|
性能指标:
- ✅ 发送数据: 48,676 字节 (~47.5 KB)
- ✅ 接收数据: 48,676 字节
- ✅ 丢包率: 0%
- ✅ 发送包数: 936 个
- ✅ 接收包数: 936 个
- ✅ 平均延迟: 0.75 µs (702,733 ns / 936)
结论: TCP ACK 包正常返回,延迟极低,说明反向路径畅通。
Flow 3: UDP Echo 请求 (Pod1 → Pod0)
1
2
3
4
| 源地址: 10.1.0.1 (Pod1.Server0)
目标地址: 10.0.0.1 (Pod0.Server0)
协议: UDP (protocol="17")
端口: 49153 → 9
|
性能指标:
- ✅ 发送数据: 1,052 字节
- ✅ 接收数据: 1,052 字节
- ✅ 丢包率: 0%
- ✅ 延迟: 3.17 µs
结论: UDP Echo 请求成功到达服务器。
Flow 4: UDP Echo 响应 (Pod0 → Pod1)
1
2
3
4
| 源地址: 10.0.0.1 (Pod0.Server0)
目标地址: 10.1.0.1 (Pod1.Server0)
协议: UDP (protocol="17")
端口: 9 → 49153
|
性能指标:
- ✅ 发送数据: 1,052 字节
- ✅ 接收数据: 1,052 字节
- ✅ 丢包率: 0%
- ✅ 延迟: 3.13 µs
结论: UDP Echo 响应成功返回,往返延迟约 6.3 µs。
ECMP 负载均衡验证
从 FlowProbe 数据可以看出,TCP 流量 (Flow 1) 在多个探针上都有记录:
1
2
3
4
| FlowProbe 44: 935 packets (549,604 bytes)
FlowProbe 46: 934 packets (547,588 bytes)
FlowProbe 60: 935 packets (549,604 bytes)
FlowProbe 62: 934 packets (547,588 bytes)
|
结论: 流量被分散到多条路径上,证明 ECMP 负载均衡生效。
整体评估
| 指标 |
结果 |
状态 |
| |
|
|
| 跨 Pod 连通性 |
4 个 Pod 之间完全连通 |
✅ 正常 |
| 丢包率 |
0% |
✅ 优秀 |
| 延迟 |
微秒级 (0.77~48 µs) |
✅ 优秀 |
| TCP 吞吐量 |
~12.4 MB/s |
✅ 正常 |
| ECMP 负载均衡 |
流量分布在多条路径 |
✅ 生效 |
| UDP Echo |
往返成功 |
✅ 正常 |
总结: 仿真结果完全正常,Fat-Tree 拓扑工作正常,ECMP 路由生效。
代码结构详解
整体架构
代码按照 9 个主要步骤组织:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
| 1. 配置模拟参数
├── 命令行参数解析
├── 时间精度设置
├── 日志配置
└── ECMP 路由配置
2. 定义链路助手
├── 服务器到接入交换机 (10 Gbps, 200 ns)
├── 接入到汇聚交换机 (40 Gbps, 70 ns)
└── 汇聚到核心交换机 (40 Gbps, 50 ns)
3. 创建节点
├── 4 个 Pod (每个 8 个节点)
├── 4 个核心交换机
├── 移动性模型配置
└── TCP/IP 协议栈安装
4. 构建拓扑
├── Pod 内部连接
└── 跨 Pod 核心层连接
5. 分配 IP 地址
├── Pod 内链路: 10.PodID.SubnetID.0/30
└── 核心层链路: 10.10.SubnetID.0/30
6. 计算路由表
└── Ipv4GlobalRoutingHelper::PopulateRoutingTables()
7. 部署应用程序
├── UDP Echo (Pod0 ↔ Pod1)
└── TCP BulkSend (Pod3 → Pod2)
8. 配置监控
├── PCAP 数据包捕获
├── FlowMonitor 统计
└── NetAnim 动画
9. 运行仿真
├── 启动仿真器
├── 导出统计数据
└── 清理资源
|
关键代码段解析
1. 链路配置
1
2
3
4
| // 服务器到接入交换机 (边缘链路)
PointToPointHelper NodeToSW;
NodeToSW.SetDeviceAttribute("DataRate", StringValue("10Gbps"));
NodeToSW.SetChannelAttribute("Delay", StringValue("200ns"));
|
设计考虑:
- 带宽: 10 Gbps 模拟标准服务器网卡
- 延迟: 200 ns 模拟短距离光纤/铜缆
- 队列: 使用默认值,因为边缘流量较小
1
2
3
4
5
6
| // 汇聚到核心交换机 (上行链路)
PointToPointHelper SWToSW_50ns;
SWToSW_50ns.SetDeviceAttribute("DataRate", StringValue("40Gbps"));
SWToSW_50ns.SetChannelAttribute("Delay", StringValue("50ns"));
SWToSW_50ns.SetQueue("ns3::DropTailQueue", "MaxSize",
StringValue(std::to_string(Corequeuesize) + "p"));
|
设计考虑:
- 带宽: 40 Gbps 模拟高速交换机互联
- 延迟: 50 ns 模拟核心层短距离连接
- 队列: 8 个数据包,防止突发流量丢包
2. 节点创建与协议栈安装
1
2
3
4
5
6
7
8
9
10
11
12
| // 创建 Pod 节点
NodeContainer pod0, pod1, pod2, pod3, core;
pod0.Create(8); // 4 服务器 + 4 交换机
// ...
// 安装 TCP/IP 协议栈
InternetStackHelper stack;
stack.Install(pod0);
stack.Install(pod1);
stack.Install(pod2);
stack.Install(pod3);
stack.Install(core);
|
关键点:
- 每个 Pod 的 8 个节点统一管理
- 所有节点都安装完整的 TCP/IP 协议栈
- 包括 IPv4、TCP、UDP、ICMP 等协议
3. 拓扑连接
1
2
3
4
5
| // Pod 0 内部连接示例
pod0_dev = NodeToSW.Install(pod0.Get(0), pod0.Get(4)); // Server0 → AccessSW4
pod0_dev2 = NodeToSW.Install(pod0.Get(1), pod0.Get(4)); // Server1 → AccessSW4
pod0_dev5 = SWToSW_50ns.Install(pod0.Get(4), pod0.Get(6)); // AccessSW4 → AggrSW6
pod0_dev6 = SWToSW_70ns.Install(pod0.Get(4), pod0.Get(7)); // AccessSW4 → AggrSW7
|
连接规律:
- 每个接入交换机连接 2 台服务器
- 每个接入交换机连接 2 个汇聚交换机(全连接)
- 提供了 Pod 内的冗余路径
1
2
3
| // 核心层连接示例
core_dev = SWToSW_50ns.Install(pod0.Get(6), core.Get(0)); // Pod0.AggrSW6 → Core0
core_dev2 = SWToSW_50ns.Install(pod1.Get(6), core.Get(0)); // Pod1.AggrSW6 → Core0
|
连接规律:
- 每个 Pod 的每个汇聚交换机连接所有核心交换机
- 提供了 Pod 间的多条等价路径(ECMP 的基础)
4. IP 地址分配
1
2
3
4
5
6
7
8
9
10
| // Pod 0 的 IP 分配
address.SetBase("10.0.0.0","255.255.255.252"); // Server0-AccessSW4
pod0_Iface = address.Assign(pod0_dev);
address.SetBase("10.0.1.0","255.255.255.252"); // Server1-AccessSW4
pod0_Iface2 = address.Assign(pod0_dev2);
// ...
// 核心层的 IP 分配
address.SetBase("10.10.0.0","255.255.255.252"); // Pod0.AggrSW6-Core0
core_Iface = address.Assign(core_dev);
|
关键技巧:
- 每次
Assign 前都调用 SetBase 重置地址计数器
- 避免
Ipv4AddressHelper 的地址溢出问题
- 使用
/30 子网 (255.255.255.252) 为 P2P 链路提供 2 个可用地址
5. 路由配置
1
2
3
4
5
| // 启用 ECMP
Config::SetDefault("ns3::Ipv4GlobalRouting::RandomEcmpRouting", BooleanValue(true));
// 填充路由表
Ipv4GlobalRoutingHelper::PopulateRoutingTables();
|
工作原理:
PopulateRoutingTables() 在所有节点上运行 Dijkstra 算法
- 计算到每个目标网络的最短路径
- 当存在多条等价路径时,ECMP 会随机选择一条
- 实现了自动的负载均衡
6. 应用部署
1
2
3
4
5
6
7
8
9
10
11
12
| // UDP Echo 服务器 (Pod0.Server0)
UdpEchoServerHelper echoServer(9);
ApplicationContainer serverApps = echoServer.Install(pod0.Get(0));
serverApps.Start(Seconds(1.0));
serverApps.Stop(Seconds(10.0));
// UDP Echo 客户端 (Pod1.Server0)
UdpEchoClientHelper echoClient(pod0_Iface.GetAddress(0), 9);
echoClient.SetAttribute("MaxPackets", UintegerValue(1));
echoClient.SetAttribute("PacketSize", UintegerValue(1024));
ApplicationContainer clientApps = echoClient.Install(pod1.Get(0));
clientApps.Start(Seconds(2.0));
|
测试目的:
- 验证跨 Pod 的基本连通性
- 测量往返延迟 (RTT)
- 确认路由表正确性
1
2
3
4
5
6
7
8
9
10
11
12
| // TCP BulkSend 发送端 (Pod3.Server0)
BulkSendHelper source("ns3::TcpSocketFactory",
InetSocketAddress(pod2_Iface.GetAddress(0), 80));
source.SetAttribute("MaxBytes", UintegerValue(1000000)); // 1 MB
ApplicationContainer sourceApps = source.Install(pod3.Get(0));
sourceApps.Start(Seconds(1.5));
// TCP PacketSink 接收端 (Pod2.Server0)
PacketSinkHelper sink("ns3::TcpSocketFactory",
InetSocketAddress(Ipv4Address::GetAny(), 80));
ApplicationContainer sinkApp = sink.Install(pod2.Get(0));
sinkApp.Start(Seconds(1.0));
|
测试目的:
- 测试大流量传输性能
- 验证 ECMP 负载均衡效果
- 测量吞吐量和延迟
7. 监控配置
1
2
3
4
5
6
7
8
9
10
| // PCAP 数据包捕获
NodeToSW.EnablePcapAll("DCN_FatTree_CSMA_Pcap");
// FlowMonitor 统计
FlowMonitorHelper flowmonHelper;
flowmonHelper.InstallAll();
// NetAnim 动画
AnimationInterface anim("animation.xml");
anim.SetConstantPosition(pod0.Get(0), 1.0, 20.0); // 设置节点位置
|
输出文件:
DCN_FatTree_CSMA_Pcap-*.pcap: Wireshark 可读的数据包文件
DCN_FatTree_FlowStat.flowmon: 流量统计 XML 文件
animation.xml: NetAnim 动画文件
关键技术点
1. Fat-Tree 拓扑特性
对称性
- 每个 Pod 的结构完全相同
- 每个服务器到任意其他服务器的路径数量相同
- 保证了公平的带宽分配
多路径
对于跨 Pod 通信(如 Pod0.Server0 → Pod2.Server0),存在多条等价路径:
1
2
3
4
5
| 路径 1: Server0 → AccessSW4 → AggrSW6 → Core0 → AggrSW6 → AccessSW4 → Server0
路径 2: Server0 → AccessSW4 → AggrSW6 → Core1 → AggrSW6 → AccessSW4 → Server0
路径 3: Server0 → AccessSW4 → AggrSW7 → Core2 → AggrSW6 → AccessSW4 → Server0
路径 4: Server0 → AccessSW4 → AggrSW7 → Core3 → AggrSW6 → AccessSW4 → Server0
... (共 8 条等价路径)
|
无阻塞
- 理论上可以实现全二分带宽 (Full Bisection Bandwidth)
- 任意一半服务器可以同时以全速与另一半通信
2. ECMP 路由机制
工作原理
- 路由表计算: 全局路由助手为每个节点计算到所有目标的最短路径
- 等价路径识别: 当存在多条相同代价的路径时,标记为等价路径
- 流量分配: 根据流的五元组 (源IP、目标IP、源端口、目标端口、协议) 哈希选择路径
- 负载均衡: 不同的流会被分配到不同的路径上
优势
- 简单: 不需要复杂的流量工程
- 高效: 利用了网络的所有可用路径
- 公平: 长期来看,流量分布均匀
局限
- 流粒度: 同一个流的所有数据包走相同路径(避免乱序)
- 哈希碰撞: 可能导致某些路径过载
- 大象流: 单个大流可能占满一条路径
3. P2P 链路与子网划分
为什么使用 P2P?
- 物理隔离: 每条链路独立,不会相互干扰
- 简化路由: 不需要 ARP,直接转发
- 性能: 无冲突,无竞争
- 兼容性: 与
Ipv4GlobalRouting 完美配合
为什么使用 /30 子网?
- 地址效率: 每个 /30 子网提供 2 个可用地址(.1 和 .2)
- 兼容性: ns-3 的
Ipv4AddressHelper 完全支持
- 清晰性: 每条链路有独立的子网,便于调试
地址分配示例
1
2
3
4
| 10.0.0.0/30:
- 网络地址: 10.0.0.0
- 可用地址: 10.0.0.1 (Server0), 10.0.0.2 (AccessSW4)
- 广播地址: 10.0.0.3
|
4. 队列管理
DropTailQueue
- 策略: FIFO (先进先出)
- 丢包: 队列满时丢弃新到达的数据包
- 简单: 实现简单,开销低
- 公平性: 对所有流一视同仁
队列大小设置
1
2
| Corequeuesize = 8; // 核心层: 8 个数据包
Leafqueuesize = 4; // 接入/汇聚层: 4 个数据包
|
设计考虑:
- 核心层队列更大,因为汇聚了更多流量
- 队列过大会增加延迟
- 队列过小会增加丢包率
- 需要根据实际流量模式调整
IP 地址分配方案
规律化设计
本项目采用了高度规律化的 IP 地址分配方案,便于理解、调试和扩展。
地址结构
1
2
3
4
5
| 10.A.B.C/30
│ │ │ └─ 主机位 (1-2)
│ │ └─── 子网 ID (0-15)
│ └───── Pod ID (0-3) 或 10 (核心层)
└─────── 固定为 10
|
Pod 内地址分配
| Pod |
子网范围 |
用途 |
| Pod 0 |
10.0.0.0/30 ~ 10.0.7.0/30 |
Pod 内 8 条链路 |
| Pod 1 |
10.1.0.0/30 ~ 10.1.7.0/30 |
Pod 内 8 条链路 |
| Pod 2 |
10.2.0.0/30 ~ 10.2.7.0/30 |
Pod 内 8 条链路 |
| Pod 3 |
10.3.0.0/30 ~ 10.3.7.0/30 |
Pod 内 8 条链路 |
Pod 内子网分配细节 (以 Pod 0 为例)
| 子网 |
链路 |
地址 1 |
地址 2 |
10.0.0.0/30 |
Server0 ↔ AccessSW4 |
10.0.0.1 |
10.0.0.2 |
10.0.1.0/30 |
Server1 ↔ AccessSW4 |
10.0.1.1 |
10.0.1.2 |
10.0.2.0/30 |
Server2 ↔ AccessSW5 |
10.0.2.1 |
10.0.2.2 |
10.0.3.0/30 |
Server3 ↔ AccessSW5 |
10.0.3.1 |
10.0.3.2 |
10.0.4.0/30 |
AccessSW4 ↔ AggrSW6 |
10.0.4.1 |
10.0.4.2 |
10.0.5.0/30 |
AccessSW4 ↔ AggrSW7 |
10.0.5.1 |
10.0.5.2 |
10.0.6.0/30 |
AccessSW5 ↔ AggrSW6 |
10.0.6.1 |
10.0.6.2 |
10.0.7.0/30 |
AccessSW5 ↔ AggrSW7 |
10.0.7.1 |
10.0.7.2 |
核心层地址分配
| 子网 |
链路 |
地址 1 |
地址 2 |
10.10.0.0/30 |
Pod0.AggrSW6 ↔ Core0 |
10.10.0.1 |
10.10.0.2 |
10.10.1.0/30 |
Pod1.AggrSW6 ↔ Core0 |
10.10.1.1 |
10.10.1.2 |
10.10.2.0/30 |
Pod2.AggrSW6 ↔ Core0 |
10.10.2.1 |
10.10.2.2 |
10.10.3.0/30 |
Pod3.AggrSW6 ↔ Core0 |
10.10.3.1 |
10.10.3.2 |
10.10.4.0/30 |
Pod0.AggrSW6 ↔ Core1 |
10.10.4.1 |
10.10.4.2 |
| … |
… |
… |
… |
10.10.15.0/30 |
Pod3.AggrSW7 ↔ Core3 |
10.10.15.1 |
10.10.15.2 |
服务器 IP 地址快速查询表
| 服务器 |
IP 地址 |
所属 Pod |
连接的交换机 |
| Pod0.Server0 |
10.0.0.1 |
Pod 0 |
AccessSW4 |
| Pod0.Server1 |
10.0.1.1 |
Pod 0 |
AccessSW4 |
| Pod0.Server2 |
10.0.2.1 |
Pod 0 |
AccessSW5 |
| Pod0.Server3 |
10.0.3.1 |
Pod 0 |
AccessSW5 |
| Pod1.Server0 |
10.1.0.1 |
Pod 1 |
AccessSW4 |
| Pod1.Server1 |
10.1.1.1 |
Pod 1 |
AccessSW4 |
| Pod1.Server2 |
10.1.2.1 |
Pod 1 |
AccessSW5 |
| Pod1.Server3 |
10.1.3.1 |
Pod 1 |
AccessSW5 |
| Pod2.Server0 |
10.2.0.1 |
Pod 2 |
AccessSW4 |
| Pod2.Server1 |
10.2.1.1 |
Pod 2 |
AccessSW4 |
| Pod2.Server2 |
10.2.2.1 |
Pod 2 |
AccessSW5 |
| Pod2.Server3 |
10.2.3.1 |
Pod 2 |
AccessSW5 |
| Pod3.Server0 |
10.3.0.1 |
Pod 3 |
AccessSW4 |
| Pod3.Server1 |
10.3.1.1 |
Pod 3 |
AccessSW4 |
| Pod3.Server2 |
10.3.2.1 |
Pod 3 |
AccessSW5 |
| Pod3.Server3 |
10.3.3.1 |
Pod 3 |
AccessSW5 |
地址规律总结
- Pod 识别: 第二个八位组直接对应 Pod ID (0, 1, 2, 3)
- 子网识别: 第三个八位组对应子网 ID (0-7 for Pod, 0-15 for Core)
- 主机识别: 第四个八位组的 .1 通常是服务器或下层设备,.2 是交换机或上层设备
- 核心层标识: 使用
10.10.x.0 便于与 Pod 内地址区分
扩展性
如果需要扩展到 k=8 Fat-Tree:
- Pod 0-7:
10.0.x.0/30 ~ 10.7.x.0/30
- 核心层:
10.10.x.0/30 ~ 10.10.63.0/30 (64 条核心链路)
运行与调试
编译项目
1
2
| cd /home/ceylan/Desktop/code/ns3/ns-3-allinone/ns-3.44
./ns3 build
|
运行仿真
1
2
3
4
5
| # 默认运行 (启用 ECMP)
./ns3 run DCN_FatTree_CSMA
# 禁用 ECMP
./ns3 run "DCN_FatTree_CSMA --ECMProuting=false"
|
使用 GDB 调试
在 VS Code 中选择 (gdb) Launch FatTree CSMA 调试配置,或在终端中:
1
| ./ns3 run DCN_FatTree_CSMA --gdb
|
查看输出文件
1. FlowMonitor 统计
1
2
3
4
5
| # 查看 XML 文件
cat DCN_FatTree_FlowStat.flowmon
# 或使用 Python 脚本解析
python3 utils/flowmon-parse-results.py DCN_FatTree_FlowStat.flowmon
|
2. PCAP 数据包分析
1
2
3
4
5
| # 使用 Wireshark 打开
wireshark DCN_FatTree_CSMA_Pcap-0-0.pcap
# 或使用 tcpdump
tcpdump -r DCN_FatTree_CSMA_Pcap-0-0.pcap -n
|
3. NetAnim 动画
1
2
| # 使用 NetAnim 打开
netanim animation.xml
|
启用详细日志
1
2
3
| // 在代码中添加
LogComponentEnable("Ipv4GlobalRouting", LOG_LEVEL_LOGIC);
LogComponentEnable("TcpSocketBase", LOG_LEVEL_INFO);
|
或在运行时:
1
| NS_LOG="Ipv4GlobalRouting=level_logic:TcpSocketBase=level_info" ./ns3 run DCN_FatTree_CSMA
|
可扩展点
1. 增加流量模式
Incast 流量 (多对一)
1
2
3
4
5
6
7
8
| // 多个服务器同时向一个服务器发送数据
for (int i = 0; i < 4; i++) {
BulkSendHelper source("ns3::TcpSocketFactory",
InetSocketAddress(pod0_Iface.GetAddress(0), 80));
source.SetAttribute("MaxBytes", UintegerValue(100000));
ApplicationContainer app = source.Install(pod1.Get(i));
app.Start(Seconds(1.0));
}
|
All-to-All 流量
1
2
3
4
5
6
7
8
| // 所有服务器互相通信
for (int src_pod = 0; src_pod < 4; src_pod++) {
for (int dst_pod = 0; dst_pod < 4; dst_pod++) {
if (src_pod != dst_pod) {
// 创建从 src_pod 到 dst_pod 的流量
}
}
}
|
2. 实现更大规模的 Fat-Tree
1
2
3
4
5
6
7
| // k=8 Fat-Tree
// 64 台服务器, 80 个交换机
int k = 8;
int num_pods = k;
int servers_per_pod = (k * k) / 4;
int switches_per_pod = k;
int core_switches = (k * k) / 4;
|
3. 添加拥塞控制算法
1
2
3
4
5
6
7
| // 使用 DCTCP
Config::SetDefault("ns3::TcpL4Protocol::SocketType",
StringValue("ns3::TcpDctcp"));
// 启用 ECN
Config::SetDefault("ns3::TcpSocketBase::UseEcn",
StringValue("On"));
|
4. 实现优先级队列
1
2
3
4
| // 使用 PrioQueue 替代 DropTailQueue
SWToSW_50ns.SetQueue("ns3::PrioQueue",
"MaxSize", StringValue("8p"),
"NumBands", UintegerValue(4));
|
5. 添加链路故障模拟
1
2
3
4
5
6
7
| // 在仿真中途断开一条链路
Simulator::Schedule(Seconds(5.0), &DisableLink, pod0_dev5);
void DisableLink(NetDeviceContainer& dev) {
dev.Get(0)->SetDown();
dev.Get(1)->SetDown();
}
|
6. 实现自定义路由算法
1
2
3
4
5
6
7
8
9
10
| // 替换全局路由为自定义路由
Ipv4StaticRoutingHelper staticRouting;
Ipv4ListRoutingHelper list;
list.Add(staticRouting, 0);
// 手动添加路由规则
Ptr<Ipv4StaticRouting> routing = staticRouting.GetStaticRouting(node->GetObject<Ipv4>());
routing->AddNetworkRouteTo(Ipv4Address("10.1.0.0"),
Ipv4Mask("255.255.0.0"),
Ipv4Address("10.0.4.2"), 1);
|
7. 性能优化
使用更高效的队列算法
1
2
3
4
5
6
7
8
9
| // RED (Random Early Detection)
SWToSW_50ns.SetQueue("ns3::RedQueue",
"MinTh", DoubleValue(5),
"MaxTh", DoubleValue(15));
// CoDel (Controlled Delay)
SWToSW_50ns.SetQueue("ns3::CoDelQueue",
"Target", TimeValue(MilliSeconds(5)),
"Interval", TimeValue(MilliSeconds(100)));
|
调整 TCP 参数
1
2
3
4
5
6
7
| // 增大 TCP 窗口大小
Config::SetDefault("ns3::TcpSocket::RcvBufSize", UintegerValue(1 << 21));
Config::SetDefault("ns3::TcpSocket::SndBufSize", UintegerValue(1 << 21));
// 使用 TCP CUBIC
Config::SetDefault("ns3::TcpL4Protocol::SocketType",
StringValue("ns3::TcpCubic"));
|
8. 添加流量分析工具
1
2
3
4
5
6
7
8
9
10
| // 自定义流量分析回调
void PacketSinkRxCallback(Ptr<const Packet> packet, const Address& from) {
std::cout << "Received packet of size " << packet->GetSize()
<< " from " << InetSocketAddress::ConvertFrom(from).GetIpv4()
<< std::endl;
}
// 连接回调
Config::ConnectWithoutContext("/NodeList/*/ApplicationList/*/$ns3::PacketSink/Rx",
MakeCallback(&PacketSinkRxCallback));
|
📚 参考资料