直观理解,在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.在实际模拟结束时间之前停止应用程序,在两者之间留出足够的时间处理队列中的数据包。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐