.jpeg)
六、Error Model 和 Flow Monitor
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 :: FlowProbe
和ns3 :: 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;
}