NS3:FlowMonitor设计讲解 和 路由协议控制开销分析 代码 (tr文件分析 和 wireshark分析)
- 需要注意的是 FlowMonitor对广播流不起作用
- 各有关类的描述与说明
- FlowMonitor模块详解
- 路由协议控制开销分析 (方法1 .tr 文件分析)
- 用flowmonitor只能用于统计点到点的流,不能用于统计 一到多的包,因为其元组定义是需要目的ip的。进而可以知道,不能用于统计路由控制信息(路由开销)(因为路由控制信息,有广播信息 有单播信息 ,如 OLSR中的HELLO包、TC包都是广播包,是无法用 flowmonitor 统计出来的; AODV的RREQ是广播包也不能统计,RREP是单播包可以统计)
- 要想统计路由开销(路由协议发出的广播包 单播包等),需要对.tr文件进行分析
- 用tr文件识别路由协议的控制开销(AODV 654端口 OLSR 698 端口 DSDV 269端口)
- 特别的对于DSR等其他特殊路由协议
- 使用本博文内容和参考代码(代码1 和 代码2)的出版物请标注本博文链接的引用
- NS3中路由协议控制开销分析 (方法二: 用wireshark 对路由协议的控制开销进行分析)
- 参考
- 一些解答
需要注意的是 FlowMonitor对广播流不起作用
flowmonitor的监控形式是 点对点,因为其定义的时候是需要目的ip的。所以它就不能跟踪广播的ip包。 olsr的控制包是广播包,所以不能跟踪olsr的控制包。博文的第二部分,添加了怎么统计路由协议的控制包,可以用 .tr文件,逐行读取 以 t 开头的,以及含有 路由协议包头的条目计数即可。就是路由协议发出的控制包的个数,含有payload就是发出的数据包的个数。 第二种方式是,生成pcap文件,合并所有的pacp文件以后,用wireshark分析包的数量,博文中给出了示例。两种方法都更给出了代码, 第一种使用更方便。
各有关类的描述与说明
-
每个仿真只有一个FlowMonitor(后面会做详细解释)
-
每个仿真只有一个FlowClassifier
- 每个流分配唯一的整数标识
- 包含映射参数的分类方法(例如,分组或分组报头)到相应的流标识符;
- FlowClassifier是抽象的,需要一个具体的子类
Ipv4FlowClassifier
-
通常(但不一定)一个节点上一个流探针FlowProbe
- 负责获取分组数据
- 与FlowClassifier合作
- FlowProbe是抽象的,需要一个具体的子类
Ipv4FlowProbe
- Ipv4FlowProbe需要一个匹配的分类器 Ipv4FlowClassifier
-
每个FlowProbe的每个模拟流程对应一个ProbeFlowStats对象来统计从第一次探测直到此时探测中接收到数据包的信息
- 按FlowId索引
- 字节数
- 分组数
- 延时
-
每个FlowMonitor的每个流对应一个EndToEndFlowStats,记录了
- 丢包数
- 丢字节数
- 总字节数
- 总包数
- 端到端延时
FlowMonitor模块详解
模块描述
Flow Monitor模块的目标是提供一个灵活的系统来衡量网络协议的性能。 该模块使用安装在网络节点中的探针来跟踪节点交换的数据包,并进行测量一些参数。 数据包根据它们所属的流进行划分,其中每个流根据探测器的特征定义(例如,对于IP包,数据被定义为五元组)
- 协议
- 源IP
- 源端口
- 目的IP
- 目的端口
收集每个流的统计信息可以以XML格式导出。 此外,用户可以直接访问探针以请求关于每个流的特定统计数据。
设计
Flow Monitor模块采用模块化设计。 它可以通过继承ns3 :: FlowProbe
和ns3 :: FlowClassifier
进行扩展。[FlowMonitor ]中描述了完整的模块设计。
适用范围和限制
目前,探针和分类器可用于IPv4和IPv6,每个探针将对分组进行跟踪和分类:
- 发送数据包 (SendOutgoing IPv[4,6] 跟踪)
- 转发数据包 (UnicastForward IPv[4,6] 跟踪)
- 收到数据包 (LocalDeliver IPv[4,6] 跟踪)
- 丢弃数据包(Drop IPv[4,6] 跟踪)
有关标签ns3::Ipv[4,6]FlowProbeTag
将被添加到数据包中,基本数据包的数据将携带标签传输,这样可对数据包进行分类。
注意:因为在IP级别跟踪分组,探针将L4协议(例如,TCP)引起的重传作为新分组; 目前只对L4(TCP,UDP)数据包分类;只有单播数据包将被分类,这些限制可能在将来被删除。
为每个流收集的数据是:
- timeFirstTxPacket: 传输流中第一个数据包时的时间
- timeLastTxPacket:传输 流中最后一个数据包时的时间
- timeFirstRxPacket:端节点接收到流中的第一个数据包的时间;
- timeLastRxPacket: 收到流中的最后一个数据包的时间
- delaySum: 所有收到的流数据包的所有端到端延迟的总和;
- jitterSum:所有接收到的流数据包的所有端到端延迟抖动(延迟变化)值的总和,参考文档:rfc:
3393
; - txBytes, txPackets: 流的传输字节/数据包总数;
- rxBytes, rxPackets: 流的接收字节/数据包总数;
- lostPackets: 假设丢失的数据包总数(未报告超过10秒);
- timesForwarded: 报告转发数据包的次数;
- delayHistogram, jitterHistogram, packetSizeHistogram: 延迟,抖动和数据包大小的直方图
- packetsDropped, bytesDropped: 丢失的数据包和字节数,根据丢失原因代码(在探测中定义)进行划分。
注意:探针测量的是包括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);
SerializeToXmlFile()
第二第三个参数分别用于激活/停用直方图和每探针详细统计数据,其他可能的替代方案可以在Doxygen文档中找到。
有关助手
助手类:ns3 :: FlowMonitorHelper 在main中只实例化一次
此外助手API遵循普通助手的模式使用。通过帮助程序,您可以在节点中安装监视器,设置监视器属性并打印统计信息。
属性设置
ns3::FlowMonitor
类中有一下属性:
- MaxPerHopDelay (Time, default 10s): 应考虑的最大每跳延迟;
- StartTime (Time, default 0s): 监控开始的时间;
- DelayBinWidth (double, default 0.001): 延迟直方图中使用的宽度;
- JitterBinWidth (double, default 0.001): 抖动直方图中使用的宽度;
- PacketSizeBinWidth (double, default 20.0): 分组直方图中使用的宽度;
- FlowInterruptionsBinWidth (double, default 0.25): 流终端直方图中使用的宽度;
- FlowInterruptionsMinTime (double, default 0.5): 最小到达间隔时间被视为流量中断。
有关输出
主要的输出是关于流统计信息的XML格式的报告。 一个例子是::
<?xml version="1.0" ?>
<FlowMonitor>
<FlowStats>
<Flow flowId="1" timeFirstTxPacket="+0.0ns" timeFirstRxPacket="+20067198.0ns" timeLastTxPacket="+2235764408.0ns" timeLastRxPacket="+2255831606.0ns" delaySum="+138731526300.0ns" jitterSum="+1849692150.0ns" lastDelay="+20067198.0ns" txBytes="2149400" rxBytes="2149400" txPackets="3735" rxPackets="3735" lostPackets="0" timesForwarded="7466">
</Flow>
</FlowStats>
<Ipv4FlowClassifier>
<Flow flowId="1" sourceAddress="10.1.3.1" destinationAddress="10.1.2.2" protocol="6" sourcePort="49153" destinationPort="50000" />
</Ipv4FlowClassifier>
<Ipv6FlowClassifier>
</Ipv6FlowClassifier>
<FlowProbes>
<FlowProbe index="0">
<FlowStats flowId="1" packets="3735" bytes="2149400" delayFromFirstProbeSum="+0.0ns" >
</FlowStats>
</FlowProbe>
<FlowProbe index="2">
<FlowStats flowId="1" packets="7466" bytes="2224020" delayFromFirstProbeSum="+199415389258.0ns" >
</FlowStats>
</FlowProbe>
<FlowProbe index="4">
<FlowStats flowId="1" packets="3735" bytes="2149400" delayFromFirstProbeSum="+138731526300.0ns" >
</FlowStats>
</FlowProbe>
</FlowProbes>
</FlowMonitor>
输出是由从10.1.3.1到10.1.2.2的TCP流生成的。
协议的代表含义(TCP/UDP, AODV/OLSR)
protocol=“6” 代表TCP
protocol=“17” 代表UDP
对于协议的 Port:654 代表 AODV ; 698 OLSR
值得注意的是,索引2探测器报告的数据包和其他探测器的字节数更多。
这是一种完全正常的行为,因为数据包在该节点的IP级别被分段。
还应该观察到接收节点的探测(索引4)不计算片段,因为重组在探测点之前完成。
已经有的使用的例子
src/flow-monitor/examples
此外,以下示例使用流量监视器的模块:
- examples/matrix-topology/matrix-topology.cc
- examples/routing/manet-routing-compare.cc
- examples/routing/simple-global-routing.cc
- examples/tcp/tcp-variants-comparison.cc
- examples/wireless/multirate.cc
- examples/wireless/wifi-hidden-terminal.cc
故障排除
不要在模拟中定义多个:ns3 :: FlowMonitorHelper
ns-3.34/examples/matrix-topology/matrix-topology.cc 出现 indefined reference
build/../examples/matrix-topology/matrix-topology.cc:297:对‘ns3::FlowMonitorHelper::FlowMonitorHelper()’未定义的引用
build/../examples/matrix-topology/matrix-topology.cc:298:对‘ns3::FlowMonitorHelper::InstallAll()’未定义的引用
build/../examples/matrix-topology/matrix-topology.cc:307:对‘ns3::FlowMonitor::SerializeToXmlFile(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, bool)’未定义的引用
build/../examples/matrix-topology/matrix-topology.cc:297:对‘ns3::FlowMonitorHelper::~FlowMonitorHelper()’未定义的引用
build/../examples/matrix-topology/matrix-topology.cc:297:对‘ns3::FlowMonitorHelper::~FlowMonitorHelper()’未定义的引用
- 引入头文件:
#include "ns3/flow-monitor-module.h"
//#include "ns3/flow-monitor.h"
//#include "ns3/flow-monitor-helper.h"
- 在example/matrix-topology/wscript 修改:
def build(bld):
obj = bld.create_ns3_program('matrix-topology',
['network', 'internet', 'netanim', 'point-to-point', 'mobility', 'applications','flow-monitor'])
obj.source = 'matrix-topology.cc'
示例代码
代码可直接查看这篇ns3利用FlowMonitor进行网络性能分析
FlowMonitor实例的分析文件:manet-routing-compare.cc
#include "ns3/flow-monitor-helper.h" //头文件加上
Ptr<FlowMonitor> flowmon;
FlowMonitorHelper flowmonHelper;
flowmon = flowmonHelper.InstallAll ();
NS_LOG_INFO ("Run Simulation.");
CheckThroughput ();
Simulator::Stop (Seconds (TotalTime));
Simulator::Run ();
flowmon->SerializeToXmlFile ((tr_name + ".flowmon").c_str(), false, false);
// flowmon->SerializeToXmlFile ((tr_name + "HestandProbes.flowmon").c_str(), true, true);// 开启每个probe和直方图
Simulator::Destroy ();
}
同时在FlowMonitor模块的example下面有:
wifi-olsr-flowmon.py 用python运行协议是UPD+OLSR
那么还有一个生成的xml文件的解析的 flowmon-parse-results.py 里面可以看到解析的 6: ‘TCP’, 17: ‘UDP’
from __future__ import division
import sys
import os
try:
from xml.etree import cElementTree as ElementTree
except ImportError:
from xml.etree import ElementTree
def parse_time_ns(tm):
if tm.endswith('ns'):
return float(tm[:-2])
raise ValueError(tm)
## FiveTuple
class FiveTuple(object):
## class variables
## @var sourceAddress
# source address
## @var destinationAddress
# destination address
## @var protocol
# network protocol
## @var sourcePort
# source port
## @var destinationPort
# destination port
## @var __slots_
# class variable list
__slots_ = ['sourceAddress', 'destinationAddress', 'protocol', 'sourcePort', 'destinationPort']
def __init__(self, el):
'''! The initializer.
@param self The object pointer.
@param el The element.
'''
self.sourceAddress = el.get('sourceAddress')
self.destinationAddress = el.get('destinationAddress')
self.sourcePort = int(el.get('sourcePort'))
self.destinationPort = int(el.get('destinationPort'))
self.protocol = int(el.get('protocol'))
## Histogram
class Histogram(object):
## class variables
## @var bins
# histogram bins
## @var nbins
# number of bins
## @var number_of_flows
# number of flows
## @var __slots_
# class variable list
__slots_ = 'bins', 'nbins', 'number_of_flows'
def __init__(self, el=None):
'''! The initializer.
@param self The object pointer.
@param el The element.
'''
self.bins = []
if el is not None:
#self.nbins = int(el.get('nBins'))
for bin in el.findall('bin'):
self.bins.append( (float(bin.get("start")), float(bin.get("width")), int(bin.get("count"))) )
## Flow
class Flow(object):
## class variables
## @var flowId
# delay ID
## @var delayMean
# mean delay
## @var packetLossRatio
# packet loss ratio
## @var rxBitrate
# receive bit rate
## @var txBitrate
# transmit bit rate
## @var fiveTuple
# five tuple
## @var packetSizeMean
# packet size mean
## @var probe_stats_unsorted
# unsirted probe stats
## @var hopCount
# hop count
## @var flowInterruptionsHistogram
# flow histogram
## @var rx_duration
# receive duration
## @var __slots_
# class variable list
__slots_ = ['flowId', 'delayMean', 'packetLossRatio', 'rxBitrate', 'txBitrate',
'fiveTuple', 'packetSizeMean', 'probe_stats_unsorted',
'hopCount', 'flowInterruptionsHistogram', 'rx_duration']
def __init__(self, flow_el):
'''! The initializer.
@param self The object pointer.
@param flow_el The element.
'''
self.flowId = int(flow_el.get('flowId'))
rxPackets = float(flow_el.get('rxPackets'))
txPackets = float(flow_el.get('txPackets'))
tx_duration = (parse_time_ns (flow_el.get('timeLastTxPacket')) - parse_time_ns(flow_el.get('timeFirstTxPacket')))*1e-9
rx_duration = (parse_time_ns (flow_el.get('timeLastRxPacket')) - parse_time_ns(flow_el.get('timeFirstRxPacket')))*1e-9
self.rx_duration = rx_duration
self.probe_stats_unsorted = []
if rxPackets:
self.hopCount = float(flow_el.get('timesForwarded')) / rxPackets + 1
else:
self.hopCount = -1000
if rxPackets:
self.delayMean = float(flow_el.get('delaySum')[:-2]) / rxPackets * 1e-9
self.packetSizeMean = float(flow_el.get('rxBytes')) / rxPackets
else:
self.delayMean = None
self.packetSizeMean = None
if rx_duration > 0:
self.rxBitrate = float(flow_el.get('rxBytes'))*8 / rx_duration
else:
self.rxBitrate = None
if tx_duration > 0:
self.txBitrate = float(flow_el.get('txBytes'))*8 / tx_duration
else:
self.txBitrate = None
lost = float(flow_el.get('lostPackets'))
#print "rxBytes: %s; txPackets: %s; rxPackets: %s; lostPackets: %s" % (flow_el.get('rxBytes'), txPackets, rxPackets, lost)
if rxPackets == 0:
self.packetLossRatio = None
else:
self.packetLossRatio = (lost / (rxPackets + lost))
interrupt_hist_elem = flow_el.find("flowInterruptionsHistogram")
if interrupt_hist_elem is None:
self.flowInterruptionsHistogram = None
else:
self.flowInterruptionsHistogram = Histogram(interrupt_hist_elem)
## ProbeFlowStats
class ProbeFlowStats(object):
## class variables
## @var probeId
# probe ID
## @var packets
# network packets
## @var bytes
# bytes
## @var delayFromFirstProbe
# delay from first probe
## @var __slots_
# class variable list
__slots_ = ['probeId', 'packets', 'bytes', 'delayFromFirstProbe']
## Simulation
class Simulation(object):
## class variables
## @var flows
# list of flows
def __init__(self, simulation_el):
'''! The initializer.
@param self The object pointer.
@param simulation_el The element.
'''
self.flows = []
FlowClassifier_el, = simulation_el.findall("Ipv4FlowClassifier")
flow_map = {}
for flow_el in simulation_el.findall("FlowStats/Flow"):
flow = Flow(flow_el)
flow_map[flow.flowId] = flow
self.flows.append(flow)
for flow_cls in FlowClassifier_el.findall("Flow"):
flowId = int(flow_cls.get('flowId'))
flow_map[flowId].fiveTuple = FiveTuple(flow_cls)
for probe_elem in simulation_el.findall("FlowProbes/FlowProbe"):
probeId = int(probe_elem.get('index'))
for stats in probe_elem.findall("FlowStats"):
flowId = int(stats.get('flowId'))
s = ProbeFlowStats()
s.packets = int(stats.get('packets'))
s.bytes = float(stats.get('bytes'))
s.probeId = probeId
if s.packets > 0:
s.delayFromFirstProbe = parse_time_ns(stats.get('delayFromFirstProbeSum')) / float(s.packets)
else:
s.delayFromFirstProbe = 0
flow_map[flowId].probe_stats_unsorted.append(s)
def main(argv):
file_obj = open(argv[1])
print("Reading XML file ", end=" ")
sys.stdout.flush()
level = 0
sim_list = []
for event, elem in ElementTree.iterparse(file_obj, events=("start", "end")):
if event == "start":
level += 1
if event == "end":
level -= 1
if level == 0 and elem.tag == 'FlowMonitor':
sim = Simulation(elem)
sim_list.append(sim)
elem.clear() # won't need this any more
sys.stdout.write(".")
sys.stdout.flush()
print(" done.")
for sim in sim_list:
for flow in sim.flows:
t = flow.fiveTuple
proto = {6: 'TCP', 17: 'UDP'} [t.protocol]
print("FlowID: %i (%s %s/%s --> %s/%i)" % \
(flow.flowId, proto, t.sourceAddress, t.sourcePort, t.destinationAddress, t.destinationPort))
if flow.txBitrate is None:
print("\tTX bitrate: None")
else:
print("\tTX bitrate: %.2f kbit/s" % (flow.txBitrate*1e-3,))
if flow.rxBitrate is None:
print("\tRX bitrate: None")
else:
print("\tRX bitrate: %.2f kbit/s" % (flow.rxBitrate*1e-3,))
if flow.delayMean is None:
print("\tMean Delay: None")
else:
print("\tMean Delay: %.2f ms" % (flow.delayMean*1e3,))
if flow.packetLossRatio is None:
print("\tPacket Loss Ratio: None")
else:
print("\tPacket Loss Ratio: %.2f %%" % (flow.packetLossRatio*100))
if __name__ == '__main__':
main(sys.argv)
处理xml
如我用协议AODV的话 Port就是 654
还有一个例子,模拟业务流量的时候用的是端口9,这里 Port 9是服务器端口,客户端和服务器之间的区分在于 客户端是请求应用的发起者,然后服务器朝着客户端发送data(实际的业务payload)信息。(当然客户端也会向服务端发些信息这都没有问题)
运行脚本中通常的数据包UDP端口 9
数据业务端口配置注意用户socket配置的是9端口。
RoutingExperiment::RoutingExperiment ()
: port (9),
····
}
····
Ptr<Socket>
RoutingExperiment::SetupPacketReceive (Ipv4Address addr, Ptr<Node> node)
{
TypeId tid = TypeId::LookupByName ("ns3::UdpSocketFactory");
Ptr<Socket> sink = Socket::CreateSocket (node, tid);
InetSocketAddress local = InetSocketAddress (addr, port);
sink->Bind (local);
sink->SetRecvCallback (MakeCallback (&RoutingExperiment::ReceivePacket, this));
return sink;
}
············
Ipv4AddressHelper addressAdhoc;
addressAdhoc.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer adhocInterfaces;
adhocInterfaces = addressAdhoc.Assign (adhocDevices);
OnOffHelper onoff1 ("ns3::UdpSocketFactory",Address ());
onoff1.SetAttribute ("OnTime", StringValue ("ns3::ConstantRandomVariable[Constant=1.0]"));
onoff1.SetAttribute ("OffTime", StringValue ("ns3::ConstantRandomVariable[Constant=0.0]"));
for (int i = 0; i < nSinks; i++)
{
Ptr<Socket> sink = SetupPacketReceive (adhocInterfaces.GetAddress (i), adhocNodes.Get (i));
AddressValue remoteAddress (InetSocketAddress (adhocInterfaces.GetAddress (i), port));
onoff1.SetAttribute ("Remote", remoteAddress);
Ptr<UniformRandomVariable> var = CreateObject<UniformRandomVariable> ();
ApplicationContainer temp = onoff1.Install (adhocNodes.Get (i + nSinks));
temp.Start (Seconds (var->GetValue (100.0,101.0)));
temp.Stop (Seconds (TotalTime));
}
路由协议控制开销分析 (方法1 .tr 文件分析)
用flowmonitor只能用于统计点到点的流,不能用于统计 一到多的包,因为其元组定义是需要目的ip的。进而可以知道,不能用于统计路由控制信息(路由开销)(因为路由控制信息,有广播信息 有单播信息 ,如 OLSR中的HELLO包、TC包都是广播包,是无法用 flowmonitor 统计出来的; AODV的RREQ是广播包也不能统计,RREP是单播包可以统计)
要想统计路由开销(路由协议发出的广播包 单播包等),需要对.tr文件进行分析
如这里所示:
aodv
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 48 10.1.1.9 > 10.1.1.255) ns3::UdpHeader (length: 28 654 > 654) ns3::aodv::TypeHeader (RREP) ns3::aodv::RrepHeader (省略) ns3::WifiMacTrailer ()
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 52 10.1.1.17 > 10.1.1.255) ns3::UdpHeader (length: 32 654 > 654) ns3::aodv::TypeHeader (RREQ) ns3::aodv::RreqHeader (省略) ns3::WifiMacTrailer ()
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 40 10.1.1.39 > 10.1.1.18) ns3::UdpHeader (length: 20 654 > 654) ns3::aodv::TypeHeader (RERR) ns3::aodv::RerrHeader (省略) ns3::WifiMacTrailer ()
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 92 10.1.1.20 > 10.1.1.10) ns3::UdpHeader (length: 72 49153 > 9) Payload (size=64) ns3::WifiMacTrailer ()
对于非广播的MAC包,如RREP,RREQ等, 以及 PayLoad包,MAC层也要传输ACK等控制包:
ns3::WifiMacHeader (CTL_ACK 省略) ns3::WifiMacTrailer ()
鉴于各层做各层的事情,在统计网络层路由协议开销的时候,MAC层发送的ACK根据情况一般不统计。 只统计网络层发送的控制包。
OLSR
对于OLSR路由协议来说,其控制包都是广播包,因此 flowmonitor 不能识别这些 olsr::MessageHeader。
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 48 10.1.1.37 > 10.1.1.255) ns3::UdpHeader (length: 28 698 > 698) ns3::olsr::PacketHeader () ns3::olsr::MessageHeader () ns3::WifiMacTrailer ()
用tr文件识别路由协议的控制开销(AODV 654端口 OLSR 698 端口 DSDV 269端口)
根据上面的分析,处理tr文件,可以根据不同端口号区分所发送的信息是 data还是控制message,可以用python额外处理或者在仿真.cc文件中运行字符串处理的函数
1.读取.tr 文件的每一行,然后识别出 t 开头的。
2. 找到其中含有各协议端口号的包,或者含有 aodv::TypeHeader/ olsr::PacketHeader/ dsdv::DsdvHeader 的包。
3. 计数即可,即为发出的网络层控制包的数量。 精细一点,也可以用字符串处理,取出其中包的大小.
特别的对于DSR等其他特殊路由协议
对于不熟悉的路由协议,建议都看一下tr文件,看看这种路由协议是从哪一层发出来的。
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 44 10.1.1.40 > 10.1.1.255) ns3::DsrRoutingHeader ( nextHeader: 17 messageType: 1 sourceId: 13 destinationId: 255 length: 16) ns3::WifiMacTrailer ()
ns3::WifiMacHeader (DATA 省略) ns3::LlcSnapHeader (省略) ns3::Ipv4Header (省略 length: 124 10.1.1.14 > 10.1.1.42) ns3::DsrRoutingHeader ( nextHeader: 17 messageType: 2 sourceId: 13 destinationId: 3 length: 24) ns3::UdpHeader (length: 72 49153 > 9) Payload (size=64) ns3::WifiMacTrailer ()
可以发现其包并不是通过UDP包发送的,而是直接通过IP包发送的,DSR是一种 IP层和UDP之间作用的。在发送数据包时,也是把UDP的包装在了Dsr包中。
在统计的时候,DsrRoutingHeader后面含有UdpHeader Payload 的即为数据包,不含的则为控制包。
使用本博文内容和参考代码(代码1 和 代码2)的出版物请标注本博文链接的引用
(在仿真.cc文件中添加)
代码1 如下
代码1.1 函数分析.tr文件分析
// huph huph197@163.com
// 统计tr文件 声明一个函数
void SendCount(std::string _path, uint64_t *SendNumArray)
{
std::string str;
std::string begin;
SendNumArray[0]=0;
SendNumArray[1]=0;
//uint64_t sTotal=0;
std:: ifstream in;
in.open(_path.c_str());
while(!in.eof())
{
std::getline(in,str);
begin=str.substr(0,1);
if(begin=="t")
{
if(str.find("Ipv4Header")) //不看mac层以下
{
//cout<<begin<<endl;
if(str.find("Payload")<100000)// 找到了数据包
//cout <<"发送和转发的数据包" <<endl;
SendNumArray[0]++;
if(str.find("aodv")||str.find("olsr")||str.find("dsdv")||str.find("dsr"))
//cout <<"发送和转发的控制包(控制包可以当做控制开销的衡量)" <<endl;
SendNumArray[1]++;
/****************/
//找长度
}
/***************/
}
}
in.close();
}
代码1.2 调用函数分析.tr,获取统计值
在统计部分: totalRx 是 flowmonitor 统计的收到的(源节点到目的节点的)数据包个数。
// huph huph197@163.com
uint64_t SendArray[2];//Array[0]发送和转发的数据包;Array[1]发送和转发的控制包
SendCount("你的tr文件名.tr",SendArray);
double normalcost = (double) SendArray[1]/( SendArray[1] +totalRx);
NS_LOG_UNCOND("normalcost:" << normalcost);
NS3中路由协议控制开销分析 (方法二: 用wireshark 对路由协议的控制开销进行分析)
在代码中添加 pcap跟踪组件
// wifiPhy.EnablePcap("mannet-first",adhocDevices,false);
// wifiPhy.EnablePcapAll("mannet-second",false);
// wifiPhy.EnablePcap("mannet-third",adhocDevices,true);
wifiPhy.EnablePcapAll("mannet-forth",true);
// void WifiPhyHelper::EnablePcapInternal (std::string prefix, Ptr<NetDevice> nd, bool promiscuous, bool explicitFilename)
输出文件名为:
mannet-forth-0-0.pcap mannet-forth-1-0.pcap mannet-forth-2-0.pcap mannet-forth-3-0.pcap mannet-forth-4-0.pcap mannet-forth-5-0.pcap mannet-forth-6-0.pcap mannet-forth-7-0.pcap mannet-forth-8-0.pcap mannet-forth-9-0.pcap mannet-forth-10-0.pcap mannet-forth-11-0.pcap mannet-forth-12-0.pcap mannet-forth-13-0.pcap mannet-forth-14-0.pcap mannet-forth-15-0.pcap mannet-forth-16-0.pcap mannet-forth-17-0.pcap mannet-forth-18-0.pcap mannet-forth-19-0.pcap mannet-forth-20-0.pcap mannet-forth-21-0.pcap mannet-forth-22-0.pcap mannet-forth-23-0.pcap mannet-forth-24-0.pcap mannet-forth-25-0.pcap mannet-forth-26-0.pcap mannet-forth-27-0.pcap mannet-forth-28-0.pcap mannet-forth-29-0.pcap mannet-forth-30-0.pcap mannet-forth-31-0.pcap mannet-forth-32-0.pcap mannet-forth-33-0.pcap mannet-forth-34-0.pcap mannet-forth-35-0.pcap mannet-forth-36-0.pcap mannet-forth-37-0.pcap mannet-forth-38-0.pcap mannet-forth-39-0.pcap mannet-forth-40-0.pcap mannet-forth-41-0.pcap mannet-forth-42-0.pcap mannet-forth-43-0.pcap mannet-forth-44-0.pcap mannet-forth-45-0.pcap mannet-forth-46-0.pcap mannet-forth-47-0.pcap mannet-forth-48-0.pcap mannet-forth-49-0.pcap
跟踪了50个节点,设备0上的发送行为
统计全部的pcap文件,需要对其进行合并,把这些文件放到一个文件夹中,比如pcaptemp下。
建立一个bash脚本,赋予运行权限后运行。
代码2 合并pcap文件的脚本
# huph huph197@163.com
path1="/home/hph/ns3.32/ns-3.32/pcaptemp"
cd $path1
current_path=$(pwd)
echo "当前路径是:$current_path"
for i in $(seq 0 1 49)
do
printf mannet-forth-$i-0.pcap
printf " "
# mergecap -w Merge.pcap Merge.pcap mannet-forth-0-0.pcap mannet-forth-1-0.pcap
done
mergecap -w Merge.pcap mannet-forth-0-0.pcap mannet-forth-1-0.pcap mannet-forth-2-0.pcap mannet-forth-3-0.pcap mannet-forth-4-0.pcap mannet-forth-5-0.pcap mannet-forth-6-0.pcap mannet-forth-7-0.pcap mannet-forth-8-0.pcap mannet-forth-9-0.pcap mannet-forth-10-0.pcap mannet-forth-11-0.pcap mannet-forth-12-0.pcap mannet-forth-13-0.pcap mannet-forth-14-0.pcap mannet-forth-15-0.pcap mannet-forth-16-0.pcap mannet-forth-17-0.pcap mannet-forth-18-0.pcap mannet-forth-19-0.pcap mannet-forth-20-0.pcap mannet-forth-21-0.pcap mannet-forth-22-0.pcap mannet-forth-23-0.pcap mannet-forth-24-0.pcap mannet-forth-25-0.pcap mannet-forth-26-0.pcap mannet-forth-27-0.pcap mannet-forth-28-0.pcap mannet-forth-29-0.pcap mannet-forth-30-0.pcap mannet-forth-31-0.pcap mannet-forth-32-0.pcap mannet-forth-33-0.pcap mannet-forth-34-0.pcap mannet-forth-35-0.pcap mannet-forth-36-0.pcap mannet-forth-37-0.pcap mannet-forth-38-0.pcap mannet-forth-39-0.pcap mannet-forth-40-0.pcap mannet-forth-41-0.pcap mannet-forth-42-0.pcap mannet-forth-43-0.pcap mannet-forth-44-0.pcap mannet-forth-45-0.pcap mannet-forth-46-0.pcap mannet-forth-47-0.pcap mannet-forth-48-0.pcap mannet-forth-49-0.pcap
其中 mergecap -w Merge.pcap 文件1.pcap 文件1.pcap 文件2.pcap …是合并命令,-w用于按照时间顺序合并信息
合并完以后,用 wireshark打开 Merge.pcap 在统计-协议分级 中可以看出:
几种协议的包情况:
olsr
aodv
dsdv
dsr
从中获取各种协议在IP的包情况(如 分组百分比)
协议 | 用户数据包 | 用户控制包 | 地址处理 |
---|---|---|---|
olsr | 16.1 | 73.0 | 2.3 |
aodv | 14.9 | 52.3 | 5.5 |
dsdv | 16.4 | 77.8 | 0.7 |
dsr | 0.4 | 69.0 | 7.2 |
其他的有关利用wireshark的分析可以自行学习
参考
[FlowMonitor] G. Carneiro, P. Fortuna, and M. Ricardo. 2009. FlowMonitor: a network monitoring framework for the network simulator 3 (NS-3). In Proceedings of the Fourth International ICST Conference on Performance Evaluation Methodologies and Tools (VALUETOOLS '09).
为什么 FlowMonitor 不收集 ns3 中 dsr.cc 的数据
https://stackoverflow.com/questions/51800123/why-does-flowmonitor-not-collect-data-for-dsr-cc-in-ns3
一些解答
用tr解析如何寻找到网络中通过流量(数据包)最多的节点
用.tr分析,统计各个节点收到和发出(发出、转发)的包就行
- 区分发出和转发的包,解析一下,在tr文件以 t 起始的记录中, NodeList/后面就是当前节点id,然后看后面ip层的包头,发送ip和这个节点号是否对应就知道是 节点自己发出的 数据流 还是转发的数据流(注意ip号和节点号对应关系)