1、Error Model

本节记录了一些错误模型对象,这些对象通常与 NetDevice 模型相关联,它们作为 network 模块的一部分进行维护:

  • RateErrorModel

  • ListErrorModel

  • ReceiveListErrorModel

  • BurstErrorModel

  • BinaryErrorModel

错误模型用于指示根据基础(可能是随机或经验)错误模型,应将数据包视为错误。

错误模型的源代码位于 src/network/utils 目录中。

通常提供两种类型的错误模型。

  • 第一个是随机模型。在这种情况下,数据包会根据底层随机变量分布进行错误处理。

这方面的一个示例是 RateErrorModel

ns3::RateErrorModel 根据基础随机变量分布对数据包进行错误处理,默认情况下,该分布是分布在 0.0 和 1.0 之间的 UniformRandomVariable。

错误率和错误单位 (bit、byte 或 packet) 由用户设置。例如,通过将 ErrorRate 设置为 0.1 并将 ErrorUnit 设置为 “Packet”,从长远来看,大约 10% 的数据包将丢失。

另一个随机模型是 BurstErrorModel,它允许配置(随机)突发误差丢弃模式。

  • 另一种类型的模型是确定性模型,其中数据包根据特定的规定模式进行错误处理。

一个例子是 ListErrorModel,它允许用户通过列出特定的数据包 UID 来指定要出错的数据包列表。

ListErrorModel 的一个变体 ReceiveListErrorModel 允许用户按其到达顺序列出要出错的数据包(不考虑其 UID)。

BinaryErrorModel 在有错误和无错误的数据包之间交替。

错误模型是 ns-3 对象,可以使用 CreateObject<>() 的典型模式创建。它们具有配置属性。

ErrorModel 可以应用于任何地方,但通常部署在 NetDevice 模型上,以便可以模拟通道损失。

// 评估数据包并返回 true 或 false,无论数据包是否应被视为出错。某些模型可能会更改数据包位缓冲区的内容。
bool ErrorModel::IsCorrupt (Ptr<Packet> pkt)

// 重置任何状态。
void ErrorModel::Reset ()

// 启动模型
void ErrorModel::Enable ()

// 禁用模型。IsCorrupt() 将始终返回 false。
void ErrorModel::Disable ()

// 返回启用状态
bool ErrorModel::IsEnabled () const

例如在tutorials里面的第六个例子:

NetDeviceContainer devices;
devices = pointToPoint.Install(nodes);

Ptr<RateErrorModel> em = CreateObject<RateErrorModel>();
em->SetAttribute("ErrorRate", DoubleValue(0.00001));
devices.Get(1)->SetAttribute("ReceiveErrorModel", PointerValue(em));

2、Flow Monitor

2.1、简介

该模块的源代码位于目录src/flow-monitor 中。

流监控器模块的目标是提供一个灵活的系统来测量网络协议的性能。该模块使用安装在网络节点中的探针来跟踪节点交换的数据包,并将测量许多参数

数据包根据它们所属的流进行划分,其中每个流根据探测器的特征定义,例如,对于 IP,流定义为具有相同 {protocol, source (IP, port), destination (IP, port)} 的五元组的数据包。

为每个流收集的统计信息可以 XML 格式导出。此外,用户可以直接访问探测以请求有关每个流的特定统计信息。

Flow Monitor模块采用模块化设计。 它可以通过继承ns3 :: FlowProbens3 :: FlowClassifier进行扩展。ns3::FlowProbe 的子类通过侦听适当的类 Traces 来工作,然后使用自己的 ns3::FlowClassifier子类对通过每个节点的数据包进行分类。

目前,探测器和分类器仅适用于 IPv4 和 IPv6

IPv4 和 IPv6 探测将数据包分为四点:

  • 发送数据包时(SendOutgoing IPv[4,6] 跟踪)

  • 转发数据包时(UnicastForward IPv[4,6] 跟踪)

  • 收到数据包时(LocalDeliver IPv[4,6] 跟踪)

  • 丢弃数据包时 (Drop IPv[4,6] traces)

由于数据包是在 IP 级别跟踪的,因此由 L4 协议(例如 TCP)引起的任何重新传输都会被探测器视为新数据包。

将向数据包中添加一个标签 (ns3::Ipv[4,6]FlowProbeTag)。该标签将携带基本数据包的数据,对数据包的分类很有用。

为每个流收集的数据包括:

  • timeFirstTxPacket:流中第一个数据包的传输时间

  • timeLastTxPacket:流中最后一个数据包的传输时间

  • timeFirstRxPacket:终端节点接收到流中的第一个数据包的时间

  • timeLastRxPacket:收到流中最后一个数据包的时间

  • delaySum:该流中所有接收到的数据包的所有端到端延迟之和

  • jitterSum:流中所有接收的数据包的所有端到端延迟抖动(延迟变化)值的总和,如 RFC 3393 中所定义

  • txBytes, txPackets:流传输的字节/数据包总数

  • rxBytes, rxPackets:流接收的字节数/数据包总数

  • lostPackets:假定丢失的数据包总数(超过 10 秒未报告)

  • timesForwarded:数据包被报告转发的次数

  • delayHistogram、jitterHistogram、packetSizeHistogram:分别表示延迟、抖动和数据包大小的直方图版本

  • packetsDropped, bytesDropped:丢失的数据包和字节数,根据丢失原因代码(在 Probe 中定义)进行划分

注意:探针测量的是包括IP头的包字节,L2标头有关信息不包含在度量范围内。

模块的使用非常简单。帮手会处理所有事情。

 // Flow monitor
Ptr<FlowMonitor> flowMonitor;
FlowMonitorHelper flowHelper;
flowMonitor = flowHelper.InstallAll();

Simulator::Stop (Seconds(stop_time));
Simulator::Run ();

flowMonitor->SerializeToXmlFile("NameOfFile.xml", true, true);

助手类:ns3 :: FlowMonitorHelper 在main中只实例化一次

ns3::FlowMonitor有以下属性:

  • MaxPerHopDelay (Time,默认 10 秒):应考虑的最大单跳延迟;

  • StartTime (Time,默认 0s):监控开始的时间;

  • DelayBinWidth (double,默认为 0.001):延迟直方图中使用的宽度;

  • JitterBinWidth (double,默认为 0.001):抖动直方图中使用的宽度;

  • PacketSizeBinWidth (double,默认为 20.0):packetSize 直方图中使用的宽度;

  • FlowInterruptionsBinWidth (double,默认为 0.25):flowInterruptions 直方图中使用的宽度;

  • FlowInterruptionsMinTime (double,默认为 0.5):被视为流中断的最小到达间隔时间。

以下来自:https://blog.csdn.net/zq563100792/article/details/142964797

2.2、平均包时延统计函数

在执行脚本中可以写入如下平均包时延计算代码,它的原理是遍历FlowMonitor统计的每一条流,其中记录的流中每个包时延信息的总和在it.second.delaySum中,累加记录到delaySum中,将它除以每条流的接收数据包的数目it.second.rxPackets的总和packetnum,可以得到总的平均包时延。

void ComputePacketDelay(Ptr<FlowMonitor> fm)
{
  // 获取统计结果
  FlowMonitor::FlowStatsContainer flowStats = fm->GetFlowStats();

  double delaySum = 0.0;
  int packetnum = 0;

  // 计算每个流的数据包时延
  for (auto it : flowStats)
  {
    if (it.second.txPackets > 0 && it.second.rxPackets > 0)
    {
      // 计算平均时延
      packetnum += it.second.rxPackets;
      delaySum += it.second.delaySum.GetSeconds();
    }
  }


  double overallAverageDelay = delaySum / packetnum * 1000;

  // 计算总体平均包时延
  std::cout << "Overall Average Packet Delay: " << overallAverageDelay << " ms" << std::endl;
}

在main函数中,仿真开始前为所有节点安装FlowMonitor,在仿真结束后可以执行统计函数输出统计结果。

FlowMonitorHelper fmHelper;//安装流监控器
Ptr<FlowMonitor> fm = fmHelper.InstallAll();

Simulator::Stop (Seconds(1500));
Simulator::Run ();
Simulator::Destroy ();
	
//统计函数
ComputePacketDelay(fm);
return 0;

2.3、丢包率统计函数

在执行脚本中可以写入如下丢包率计算代码,它的原理是遍历FlowMonitor统计的每一条流,其中记录的流中发送包的数目在it.second.txPackets中,减去记录的被接收的包数量it.second.rxPackets即可得到丢包数目,除以总发包数即为丢包率。

void ComputePacketLossRate(Ptr<FlowMonitor> fm)
{
  // 获取统计结果
  FlowMonitor::FlowStatsContainer flowStats = fm->GetFlowStats();

  uint32_t totalSentPackets = 0;
  uint32_t totalReceivedPackets = 0;

  // 计算每个流的数据包发送和接收数量
  for (auto it : flowStats)
  {
    if (it.second.txPackets > 0)
    {
      totalSentPackets += it.second.txPackets;
      totalReceivedPackets += it.second.rxPackets;
    }
  }

  // 计算丢包率
  uint32_t lostPackets = totalSentPackets - totalReceivedPackets;
  double packetLossRate = static_cast<double>(lostPackets) / totalSentPackets * 100.0;

  std::cout << "Total Sent Packets: " << totalSentPackets << std::endl;
  // std::cout << "Total Received Packets: " << totalReceivedPackets << std::endl;
  // std::cout << "Lost Packets: " << lostPackets << std::endl;
  std::cout << "Packet Loss Rate: " << packetLossRate << "%" << std::endl;
}

2.4、流完成时间统计函数

在执行脚本中可以写入如下流吞吐量计算代码,它的原理是遍历FlowMonitor统计的每一条流,其中记录的流中收到最后一个包it.second.timeLastRxPacket减去第一个包发送的时间it.second.timeFirstTxPacket可以得到流完成时间。

void ComputeFlowCompleteTime(FlowMonitorHelper &fmHelper, Ptr<FlowMonitor> fm)
{
  // 获取统计结果
  FlowMonitor::FlowStatsContainer flowStats = fm->GetFlowStats();

  // 计算完成时间
  double totalThroughput = 0.0;
  double fct = 0;
  int validFlows = 0;
  for (auto it : flowStats)
  {
    if (it.second.txBytes > 0)
    {
      fct += (it.second.timeLastRxPacket - it.second.timeFirstTxPacket).GetSeconds ()*1e3;//单位为毫秒
      validFlows++;
    }
  }

  std::cout << "FCT: " << fct/validFlows << " ms" << std::endl;
}

2.5、流吞吐量统计函数

在执行脚本中可以写入如下流吞吐量计算代码,它的原理是遍历FlowMonitor统计的每一条流,其中记录的流中收到最后一个包it.second.timeLastRxPacket减去第一个包发送的时间it.second.timeFirstTxPacket可以得到流完成时间,用流的接收数据量it.second.rxBytes(单位是B字节,记得乘以8换算成bytes)除以流完成时间,即可得到流吞吐量。

void ComputeFlowThroughput(FlowMonitorHelper &fmHelper, Ptr<FlowMonitor> fm)
{
  // 获取统计结果
  FlowMonitor::FlowStatsContainer flowStats = fm->GetFlowStats();

  // 计算总吞吐量
  double totalThroughput = 0.0;
  int validFlows = 0;
  for (auto it : flowStats)
  {
    if (it.second.txBytes > 0)
    {
      // 确保时间间隔非零
      Time duration = it.second.timeLastRxPacket - it.second.timeFirstTxPacket;
      if (duration.GetSeconds() > 0)
      {
        double throughput = it.second.rxBytes * 8.0 / duration.GetSeconds() / 1024 / 1024; // 比特转换为Mbps
        totalThroughput += throughput;
        //std::cout << "Flow ID: " << it.first << ", Throughput: " << throughput << " Mbps" << std::endl;
      }
      validFlows++;
    }
  }

  std::cout << "Total Network Throughput: " << totalThroughput << " Mbps" << std::endl;
  std::cout << "Avg Network Throughput: " << totalThroughput/validFlows << " Mbps" << std::endl;
}

2.6、网络吞吐量统计函数

网络吞吐量一般被定义为网络单位时间内接收到的数据量,首先统计接收总数据量,是流接收量it.second.rxBytes的累加,之后计算最大的it.second.timeLastRxPacket,为网络收到最后一个包的时间,总接收量除以完成时间即可得到网络吞吐量。

void ComputeNetThroughput(FlowMonitorHelper &fmHelper, Ptr<FlowMonitor> fm)
{
  // 获取统计结果
  FlowMonitor::FlowStatsContainer flowStats = fm->GetFlowStats();

  // 计算总吞吐量
  double totalrxbyte = 0.0;
  double lastfct = 0;
  for (auto it : flowStats)
  {

    if (it.second.txBytes > 0)
    {
      // 确保时间间隔非零
      Time duration = it.second.timeLastRxPacket - it.second.timeFirstTxPacket;
      if (duration.GetSeconds() > 0)
      {
        double rxbyte = it.second.txBytes * 8.0; // 比特
        totalrxbyte += rxbyte;
      }

      if(it.second.timeLastRxPacket.GetSeconds() > lastfct){
        lastfct = it.second.timeLastRxPacket.GetSeconds();
      }
    }
  }

  std::cout << "Network Throughput: " << totalrxbyte/lastfct/1024/1024 << "Mbps" << std::endl;
  
}