直观理解,在ns3里面如果想要统计时延、丢包率、带宽,需要在收发双方记录数据包,并打上时间戳,最后汇总一下数据
一、原理
FlowMonitor是利用probe实现的跟踪数据包,可以收集的数据:
①时间戳
-
timeFirstTxPacket: when the first packet in the flow was transmitted;
-
timeLastTxPacket: when the last packet in the flow was transmitted;
-
timeFirstRxPacket: when the first packet in the flow was received by an end node;
-
timeLastRxPacket: when the last packet in the flow was received;
②QoS指标
-
端到端时延delaySum: the sum of all end-to-end delays for all received packets of the flow;
-
端到端抖动(时延方差)jitterSum: the sum of all end-to-end delay jitter (delay variation) values for all received packets of the flow, as defined in RFC 3393;
-
发送字节数、数据包个数txBytes, txPackets: total number of transmitted bytes / packets for the flow;
-
接收字节数、数据包个数rxBytes, rxPackets: total number of received bytes / packets for the flow;
-
丢失数据包个数lostPackets: total number of packets that are assumed to be lost (not reported over 10 seconds);
-
单个数据包被转发的次数timesForwarded: the number of times a packet has been reportedly forwarded;
-
延迟、抖动和数据包大小的直方图delayHistogram, jitterHistogram, packetSizeHistogram: histogram versions for the delay, jitter, and packet sizes, respectively;
-
丢失的数据包和字节数packetsDropped, bytesDropped: the number of lost packets and bytes, divided according to the loss reason code (defined in the probe).
二、实现步骤(以examples/routing/simple-global-routing.cc为例)
①头文件
#include "ns3/flow-monitor-helper.h"
②主要代码
官方给的模版如下:
// Flow monitor
Ptr<FlowMonitor> flowMonitor;
FlowMonitorHelper flowHelper;
flowMonitor = flowHelper.InstallAll();
-yourApplicationsContainer-.Stop(Seconds(stop_time));;
Simulator::Stop(Seconds(stop_time+cleanup_time));
Simulator::Run();
flowMonitor->SerializeToXmlFile("NameOfFile.xml", true, true);
简单实现:
// Flow Monitor
FlowMonitorHelper flowmonHelper;
if (enableFlowMonitor)
{
flowmonHelper.InstallAll ();
}
NS_LOG_INFO ("Run Simulation.");
Simulator::Stop (Seconds (11));
Simulator::Run ();
NS_LOG_INFO ("Done.");
if (enableFlowMonitor)
{
flowmonHelper.SerializeToXmlFile ("simple-global-routing.flowmon", false, false);
}
Simulator::Destroy ();
这里生成的序列化xml文件可在NetAnim中进一步解析
三、修改(打印结果,并保存为txt)
①单独定义变量
FlowId flowId = it->first;
Ipv4Address sourceIp = t.sourceAddress;
Ipv4Address destinationIp = t.destinationAddress;
uint32_t lostPackets = it->second.txPackets - it->second.rxPackets;
double delay = it->second.delaySum.GetSeconds();
double jitter = it->second.jitterSum.GetSeconds();
double throughput = it->second.rxBytes * 8.0 / (it->second.timeLastRxPacket.GetSeconds () - it->second.timeFirstTxPacket.GetSeconds ()) / 1024 / 1024;
double averageDelay = it->second.delaySum.GetSeconds() / it->second.rxPackets;
double dropsRatio = static_cast<double>(it->second.txPackets - it->second.rxPackets) / it->second.txPackets;
②保存到txt(生成的txt默认保存在ns3.35目录下)
outFile << "Flow ID: " << flowId << " Source IP: " << sourceIp << " Destination IP: " << destinationIp << std::endl;
outFile << " Lost Packets: " << lostPackets << std::endl;
outFile << " Delay (s): " << delay << std::endl;
outFile << " Jitter (s): " << jitter << std::endl;
outFile << " Throughput (Mbps): " << throughput << std::endl;
outFile << " Average Delay (s): " << averageDelay << std::endl;
outFile << " Drops Ratio: " << dropsRatio << std::endl;
③完整代码
可以使用标准的 C++ 文件操作来实现
//头文件
#include <fstream>
//数据统计,写在仿真启动和结束之间
Ptr<Ipv4FlowClassifier> classifier = DynamicCast<Ipv4FlowClassifier> (flowHelper.GetClassifier ());
std::map<FlowId, FlowMonitor::FlowStats> states = flowMonitor->GetFlowStats ();
// 打开文件
std::ofstream outFile;
outFile.open("ouput/sim13.txt");
// 输出流统计信息到文件
for (auto it = states.begin(); it != states.end(); ++it)
{
Ipv4FlowClassifier::FiveTuple t = classifier->FindFlow(it->first);
// 单独保存每个变量
FlowId flowId = it->first;
Ipv4Address sourceIp = t.sourceAddress;
Ipv4Address destinationIp = t.destinationAddress;
uint32_t lostPackets = it->second.txPackets - it->second.rxPackets;
double delay = it->second.delaySum.GetSeconds();
double jitter = it->second.jitterSum.GetSeconds();
double throughput = it->second.rxBytes * 8.0 / (it->second.timeLastRxPacket.GetSeconds () - it->second.timeFirstTxPacket.GetSeconds ()) / 1024 / 1024;
double averageDelay = it->second.delaySum.GetSeconds() / it->second.rxPackets;
double dropsRatio = static_cast<double>(it->second.txPackets - it->second.rxPackets) / it->second.txPackets;
// 将变量写入文件
outFile << "Flow ID: " << flowId << " Source IP: " << sourceIp << " Destination IP: " << destinationIp << std::endl;
outFile << " Lost Packets: " << lostPackets << std::endl;
outFile << " Delay (s): " << delay << std::endl;
outFile << " Jitter (s): " << jitter << std::endl;
outFile << " Throughput (Mbps): " << throughput << std::endl;
outFile << " Average Delay (s): " << averageDelay << std::endl;
outFile << " Drops Ratio: " << dropsRatio << std::endl;
}
// 关闭文件
outFile.close();
④运行结果
PS.关于FlowMonitor统计“lost packets”的机制:
It is important to keep in mind that Flow Monitor records the packets statistics by intercepting them at a given network level - let’s say at IP level. When the simulation ends, any packet queued for transmission below the IP level will be considered as lost.
FlowMonitor仅在IP层对数据包个数进行统计,在IP层之下的(例如在节点队列中排队等待传输的都被视为已经丢失)
这里的“丢包”其实更像是脱离FlowMonitor监控的packets,只是不在IP层,有可能还在整个网络里面(比如说还在节点的缓冲队列里)
真实的丢包产生原因应该主要是由于网络拥塞,节点的缓冲队列已经被占满,导致的被迫丢包;或者一些突发性故障导致的丢包,可以理解为这些数据包完全不在网络里了
假设传输时间足够长,节点能把网络中所有现存的数据包都转发完,此时FlowMonitor得到的丢包率才等于真实的丢包率
尽管这个数据包个数的误差可能很小,但还是需要注意,所以经常能看到实际的模拟结束时间略大于应用程序的执行时间:
Stop the Applications before the actual Simulation End time, leaving enough time between the two for the queued packets to be processed.在实际模拟结束时间之前停止应用程序,在两者之间留出足够的时间处理队列中的数据包。