这段代码是一个使用ns-3网络模拟器的示例程序,用于模拟两个节点之间的TCP数据流,并跟踪拥塞窗口的变化。
代码的主要功能包括:
- 创建两个节点和一个点对点的网络连接。
- 配置网络设备的属性,如数据传输速率和延迟。
- 安装Internet协议栈和TCP应用。
- 创建一个PacketSink应用作为接收方,用于接收来自源节点的TCP流量。
- 创建一个自定义的TutorialApp应用作为发送方,用于生成TCP流量并发送到接收方。
- 跟踪拥塞窗口的变化,并将数据写入文件。
- 跟踪接收方丢弃的数据包,并将数据写入PCAP文件。
- 使用GnuplotHelper绘制数据包字节计数随时间的变化图表。
- 使用FileHelper将数据包字节计数随时间的变化写入文件。
- 运行模拟器并销毁模拟环境。
总体而言,该代码用于演示如何在ns-3网络模拟器中创建TCP数据流,并跟踪拥塞窗口的变化以及数据包的丢失情况。
// node 0 node 1
// +----------------+ +----------------+
// | ns-3 TCP | | ns-3 TCP |
// +----------------+ +----------------+
// | 10.1.1.1 | | 10.1.1.2 |
// +----------------+ +----------------+
// | point-to-point | | point-to-point |
// +----------------+ +----------------+
// | |
// +---------------------+
// 5 Mbps, 2 ms
#include "tutorial-app.h" // 包含自定义的tutorial-app头文件
#include "ns3/applications-module.h" // 包含应用程序模块头文件
#include "ns3/core-module.h" // 包含核心模块头文件
#include "ns3/internet-module.h" // 包含网络模块头文件
#include "ns3/network-module.h" // 包含网络模块头文件
#include "ns3/point-to-point-module.h" // 包含点对点模块头文件
#include "ns3/stats-module.h" // 包含统计模块头文件
#include <fstream> // 包含文件流头文件
using namespace ns3;
NS_LOG_COMPONENT_DEFINE("SeventhScriptExample"); // 定义日志组件
// 回调函数:拥塞窗口变化
static void
CwndChange(Ptr<OutputStreamWrapper> stream, uint32_t oldCwnd, uint32_t newCwnd)
{
NS_LOG_UNCOND(Simulator::Now().GetSeconds() << "\t" << newCwnd); // 记录拥塞窗口变化
*stream->GetStream() << Simulator::Now().GetSeconds() << "\t" << oldCwnd << "\t" << newCwnd
<< std::endl; // 将拥塞窗口变化写入输出流
}
// 回调函数:接收端丢包
static void
RxDrop(Ptr<PcapFileWrapper> file, Ptr<const Packet> p)
{
NS_LOG_UNCOND("RxDrop at " << Simulator::Now().GetSeconds()); // 记录数据包丢失发生的时间
file->Write(Simulator::Now(), p); // 将丢失的数据包写入PCAP文件
}
int
main(int argc, char* argv[])
{
bool useV6 = false;
CommandLine cmd(__FILE__);
cmd.AddValue("useIpv6", "Use Ipv6", useV6);
cmd.Parse(argc, argv);
NodeContainer nodes; // 创建节点容器
nodes.Create(2); // 创建两个节点
PointToPointHelper pointToPoint; // 创建点对点网络连接助手
pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps")); // 设置链路数据速率
pointToPoint.SetChannelAttribute("Delay", StringValue("2ms")); // 设置链路延迟
NetDeviceContainer devices; // 创建网络设备容器
devices = pointToPoint.Install(nodes); // 安装网络设备
Ptr<RateErrorModel> em = CreateObject<RateErrorModel>(); // 创建错误模型
em->SetAttribute("ErrorRate", DoubleValue(0.00001)); // 设置错误率
devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em)); // 设置接收端的错误模型
InternetStackHelper stack; // 创建Internet协议栈助手
stack.Install(nodes); // 安装Internet协议栈
uint16_t sinkPort = 8080; // 数据包接收器监听的端口号
Address sinkAddress; // 接收器地址
Address anyAddress; // 任意地址
std::string probeType; // 探测类型
std::string tracePath; // 跟踪路径
if (useV6 == false)
{
Ipv4AddressHelper address; // 创建IPv4地址助手
address.SetBase("10.1.1.0", "255.255.255.0"); // 设置IPv4地址范围
Ipv4InterfaceContainer interfaces = address.Assign(devices); // 分配IPv4地址给设备
sinkAddress = InetSocketAddress(interfaces.GetAddress(1), sinkPort); // 获取接收器地址
anyAddress = InetSocketAddress(Ipv4Address::GetAny(), sinkPort); // 获取任意地址
probeType = "ns3::Ipv4PacketProbe"; // 设置探测类型为IPv4
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx"; // 设置跟踪路径
}
else
{
Ipv6AddressHelper address; // 创建IPv6地址助手
address.SetBase("2001:0000:f00d:cafe::", Ipv6Prefix(64)); // 设置IPv6地址范围
Ipv6InterfaceContainer interfaces = address.Assign(devices); // 分配IPv6地址给设备
sinkAddress = Inet6SocketAddress(interfaces.GetAddress(1), sinkPort); // 获取接收器地址
anyAddress = Inet6SocketAddress(Ipv6Address::GetAny(), sinkPort); // 获取任意地址
probeType = "ns3::Ipv6PacketProbe"; // 设置探测类型为IPv6
tracePath = "/NodeList/*/$ns3::Ipv6L3Protocol/Tx"; // 设置跟踪路径
}
PacketSinkHelper packetSinkHelper(probeType, anyAddress); // 创建数据包接收器助手
ApplicationContainer sinkApps = packetSinkHelper.Install(nodes.Get(1)); // 在接收端节点安装数据包接收器
sinkApps.Start(Seconds(0.0)); // 启动数据包接收器
sinkApps.Stop(Seconds(20.0)); // 停止数据包接收器
Ptr<Socket> ns3TcpSocket = Socket::CreateSocket(nodes.Get(0), TcpSocketFactory::GetTypeId()); // 创建一个TCP套接字,并将其指针赋值给ns3TcpSocket变量
Ptr<TutorialApp> app = CreateObject<TutorialApp>(); // 创建TutorialApp对象,并将其指针赋值给app变量
app->Setup(ns3TcpSocket, sinkAddress, 1040, 1000, DataRate("1Mbps")); // 调用TutorialApp的Setup函数,设置应用程序的参数
nodes.Get(0)->AddApplication(app); // 将应用程序app添加到节点0
app->SetStartTime(Seconds(1.)); // 设置应用程序的开始时间为1秒
app->SetStopTime(Seconds(20.)); // 设置应用程序的停止时间为20秒
AsciiTraceHelper asciiTraceHelper; // 创建AsciiTraceHelper对象,用于生成ASCII跟踪文件
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("seventh.cwnd"); // 创建输出流,用于记录拥塞窗口变化,并将其指针赋值给stream变量
ns3TcpSocket->TraceConnectWithoutContext("CongestionWindow",
MakeBoundCallback(&CwndChange, stream)); // 将拥塞窗口变化与输出流进行连接,当拥塞窗口变化时,调用CwndChange函数进行记录
PcapHelper pcapHelper; // 创建PcapHelper对象,用于生成PCAP文件
Ptr<PcapFileWrapper> file =
pcapHelper.CreateFile("seventh.pcap", std::ios::out, PcapHelper::DLT_PPP); // 创建PCAP文件,用于记录丢失的数据包,并将其指针赋值给file变量
devices.Get(1)->TraceConnectWithoutContext("PhyRxDrop", MakeBoundCallback(&RxDrop, file)); // 将数据包丢失事件与PCAP文件进行连接,当数据包丢失时,调用RxDrop函数进行记录
GnuplotHelper plotHelper; // 创建GnuplotHelper对象,用于绘制图表
plotHelper.ConfigurePlot("seventh-packet-byte-count",
"Packet Byte Count vs. Time",
"Time (Seconds)",
"Packet Byte Count"); // 配置图表的属性,包括文件名前缀、标题、x轴和y轴标签
plotHelper.PlotProbe(probeType,
tracePath,
"OutputBytes",
"Packet Byte Count",
GnuplotAggregator::KEY_BELOW); // 绘制指定的探测类型的数据,将其与跟踪源路径和输出字节计数进行连接,指定数据系列标签和图表的格式
FileHelper fileHelper; // 创建FileHelper对象,用于写入文件
fileHelper.ConfigureFile("seventh-packet-byte-count", FileAggregator::FORMATTED); // 配置要写入的文件的属性和格式化方式
fileHelper.Set2dFormat("Time (Seconds) = %.3e\tPacket Byte Count = %.0f"); // 设置文件的格式化输出标签
fileHelper.WriteProbe(probeType, tracePath, "OutputBytes"); // 写入指定的探测类型的数据到文件
Simulator::Stop(Seconds(20)); // 设置仿真的停止时间为20秒
Simulator::Run(); // 运行仿真
Simulator::Destroy(); // 销毁仿真环境
return 0;
// 返回0,表示程序执行成功
}
评论区