本文转自 https://blog.csdn.net/qq_33588730/article/details/80488870
在看这篇博客之前,建议先看另一篇自己写的前置博客,包括一些关于ndnSIM安装,源代码的目录结构,推荐使用的IDE,以及以修改兴趣包生存周期为例子的修改代码实例,属于这篇博客的前置知识:
https://blog.csdn.net/qq_33588730/article/details/81061724
这篇博客大部分从ndnSIM官网翻译而来,同时加上了自己的思考和理解,对原来的部分语句修改成了更通俗易懂一些的表述方式,希望可以理清自己做仿真的思路,同时给一些英文以及NDN基础不太好的同研究方向同学给予参考。本文基于ndnSIM的2.3版本以及C++语言。英文教程官网如下:http://ndnsim.net/2.3/index.html
1.StackHelper被用于在需要的NDN节点上安装ndnSIM网络协议栈,是在仿真场景文件(scenario)中首先需要被初始化的一个Helper类,相当于一个配置多个属性的容器,一次配置,然后一次性同时安装在多个节点上,方便管理大量节点而不需要一个个去设置。使用方式如下(具体例子在后面多次出现,一般读取完拓扑txt文件后就会使用它给所有节点配置NDN协议栈):
2.两种方法可以设置路由:
(1)手动设置FIB路由: FIBhelper通过向NFD的FIB管理器发送特殊的兴趣包指令与该FIB管理器进行交互,以便从FIB条目添加/删除下一跳,或手动向FIB添加路由(手动配置FIB)。手动添加路由到FIB的方法如下所示(出现的例子在第9点):
(2)为了简化大型拓扑中多个节点的FIB管理,ndnSIM包含一个全局路由控制器GlobalRoutingHelper(Helper和special interface),与Ipv4GlobalRoutingHelper相似。有几个必要的步骤,以利用全局路由控制器来统一同时配置多个节点:
a.在网络节点上安装 special interfaces:
b.指定哪个内容提供者节点使用GlobalRoutingHelper::AddOrigins()返回什么名称前缀的数据包:
c.使用 GlobalRoutingHelper::CalculateRoutes()计算并安装每个NDN节点的FIB(仅在涉及到Forwarding Strategy的时候使用该语句,例子第6点出现):
3.内容仓库(CS)以CSentries的一组集合来实现,按照包括隐式摘要在内的完整内容名称进行排序。为了管理条目,CS采用缓存策略接口nfd::cs::Policy,每当添加,删除,更新或使用CS条目时被触发。ndnSIM使用NFD的内容仓库实现,Content Store默认最大数据包缓存为100个。要配置CS最大容量和缓存策略,可以使用StackHelper::setCsSize()和 StackHelper::setPolicy()方法,例如下面的例子,要在节点1上设置CS大小为100,在节点2上设置CS大小为1000个内容,并在所有其他节点上设置CS大小为2000。节点1设置LRU缓存替换策略,其余节点为先进先出优先队列(FIFO):
如果要在所有节点上禁用内容仓库,可以将CS容量设置为1。如果设定为0,则会使用旧的内容仓库(OldContentStore)的实现:
旧的ndnSIM 1.0内容存储实现可以使用在新的2.0版本中。该旧实现具有多样的缓存替换策略,但对兴趣包选择器(InterestSelector)的支持非常有限。如果仿真场景依赖于兴趣包选择器的处理,不要使用旧的CS,因为模拟结果很可能不正确。要使用旧的内容仓库实现,可以使用 StackHelper::SetOldContentStore(),例如下面的例子:
(1)在节点1上选择LRU策略,在节点2上选择FIFO策略,以及在其他节点上选择随机策略,CS最大容量为10000个数据包,如果MaxSize属性省略,容量将被设为默认值100,如果设为0,则容量没有限制:
可选的旧CS缓存替换策略如下:
(2)在节点2上禁用内容仓库:
(3)跟踪CS条目的生命周期(必须使用ns3::ndn::cs::Stats::*策略):
(4)获取CS缓存命中率/未命中率的汇总统计(适用于任何策略),在仿真场景文件的Simulator::Run()之前使用CsTracer:
4.Application Helper用于确定节点的身份,如consumerCbr和Producer。Consumer和Producer都属于AppHelper,它简化了创建,配置和安装ndnSIM应用的任务。在配置内容请求者和内容提供者时,都需要通过AppHelper来创建该application及其属性,基本使用方式如下:
(1)为特定的application class创建helper:
(2)使用 AppHelper::SetPrefix()为consumer或producer指定包的名称前缀(使用此名称前缀生成兴趣包或满足包含此名称前缀的兴趣包):
(3)使用 AppHelper::SetAttribute()配置特定application的属性:
(4)在一个或多个节点上安装配置好的application:
而有些场景需要在某些时候在NDN节点之间断开某些链接。NS-3不提供实际“断开”节点之间链接的能力。但是,它提供了设置损失模型的功能,以模拟信道中的数据包丢失。通过在点对点链路两侧使用正确建立的损耗模型,可以模拟链路断开。为了简化这些操作,ndnSIM包含ndn::LinkControlHelper,它允许调度链路故障和故障恢复:
5.ndnSIM默认支持四种转发策略:
每个节点和每个名称前缀都可以采用不同的转发策略,例如:
ndn::StrategyChoiceHelper::Install(nodes,prefix, "/localhost/nfd/strategy/multicast");
或者:
ndn::StrategyChoiceHelper::InstallAll(prefix,"/localhost/nfd/strategy/multicast ");
其中prefix是名称前缀,为引号字符串,例如"/prefix"。
6.关于如何定制自己的转发策略:
该策略的目的是决定如何转发兴趣包,而不是打算覆盖NFD转发管道(pipeline)中的任何处理步骤。如果希望支持新的数据包类型(除了兴趣包和数据包),增加兴趣或数据包中的新字段,或者覆盖pipeline中的某些动作(例如,禁用ContentStore查找),则可以修改在nfd :: Forwarder类中的转发管道。
创建新策略的第一步是创建一个类(class),比如说继承nfd :: fw :: Strategy的RandomLoadBalancerStrategy。该子类必须至少重写(override)标记为纯虚函数(virtual)的触发器(如afterReceiveInterest),并可以重写其他标记为虚函数的可用触发器。例如:
如果策略需要存储信息,则需要确定信息是与命名空间(namespace)还是兴趣包(Interest)相关。与命名空间相关但不特定于某个兴趣包的信息应存储在测量入口(Measurements entries)中;与兴趣包相关的信息应存储在PIT条目,PIT下游记录或PIT上游记录中。做出这些决策之后,需要声明从StrategyInfo类继承的数据结构。在现有的实现中,这样的数据结构被声明为嵌套类。如果需要定时器(timer),则需要将EventId字段添加到此类数据结构中。
最后一步是用期望的策略逻辑实现一个或多个触发器。 下面列出了这些触发器:
(1)AfterReceive Interest Trigger():
当一个兴趣包被收到、通过必要的检查、以及需要转发时,传入兴趣包管道(Incoming Interest pipeline,NFD Developer’s Guide第4.2.1节)通过PIT条目、传入兴趣包和FIB条目来调用此触发器。 在那时,下列条件适用于该兴趣包:
a.兴趣不违反/localhost范围。
b.兴趣包没有形成环路。
c.当前节点的ContentStore无法满足兴趣包。
d.兴趣包在这个策略管理的命名空间下。
在触发函数被触发后,转发策略应该决定是否以及在何处转发此兴趣包。如果策略决定转发此兴趣包,则应至少调用一次sendInterest操作。 如果策略认为这个兴趣包不能被转发,应该调用reject pendingInterest操作,这样PIT条目很快就会被删除。使用afterReceiveInterest()触发器函数的例子如下所示:
(2)BeforeSatisfy Interest Trigger():
这种方法将在即将到来的兴趣包被满足之前触发。 例如,测量数据平面性能时。
(3)BeforeExpire Interest Trigger():
此方法将在pendingInterest超时之前触发。与之前的beforesatisfy interest触发器相同,此方法可能对测量数据平面性能有用。
在按照上面步骤创造了自己定制的转发策略的一个新类,声明并实现了需要的触发器函数之后,最后需要在仿真场景文件(scenario)中在对应网络节点和命名空间上安装自己定制的策略,如下所示:
7.ndnSIM将内容请求者(consumer)和内容提供者(producer)作为应用(Application)配置在仿真场景文件中,官方默认的几种consumer如下所示:
(1)ConsumerCbr。它是一个consumer应用,作为一个内容请求者生成恒定兴趣包产生频率、恒定平均传输速率和随机均匀分布或随机指数分布兴趣包间隔率的兴趣包。例如第7点中的配置:
对于兴趣包的产生间隔,可以选择随机间隔或者非随机,有如下选择:
a.”none”,没有随机
b.”uniform”,范围在0~1/Frequency之间的均匀分布,即兴趣包产生间隔为1/Frequency。
c.”exponential”,以1/Frequency为均值的指数分布
因此,设置兴趣包产生间隔方式的方法如下所示:
(2)ConsumerZipfMandelbrot,是在服从Zipf-Mandelbrot分布(内容频率分布的数量)基础上请求内容(请求中的名称)的应用。这个类是ConsumerCbr的一个子类。配制方法如下所示:
频率和随机分布的配置方法与其父类ConsumerCbr相同。默认内容的类型数量为100种,以正整数顺序来标注类型。
(3)ConsumerBatches是一种开关式的内容请求者应用,可在指定的模拟时刻点生成指定数量的兴趣包。使用方法如下所示:
该Application可以指定兴趣包发送的确切模式,指定何时以及发送多少兴趣包。以下示例定义在仿真时间进行到第1秒时应该请求1个兴趣包,第2秒发送5个兴趣包,第10秒发送2个兴趣包:
然而,指定的一批兴趣包不会立即在指定的时刻发出。ConsumerBatch在指定的时刻开始每个兴趣包发送批次,而每个兴趣包在时间上被估算的重传时间(retransmission time)分开。 例如,可能导致以下兴趣包发送时间表:
(4)ConsumerWindow 是生成可变速率兴趣包传输的consumer应用。 它实现了一个简单的基于滑动窗口的兴趣包生成机制。它的实现如下所示:
a.Window,默认值为1,在没有等待数据的情况下将被发送的初始兴趣包数量(未满足的兴趣包数量)。
b.PayloadSize,默认值为1040,数据包的预期大小(仅在指定了大小时才需要)。
c.Size,默认值为-1,需要请求的数据量(在收到对应数量数据包后将停止产生兴趣包)。如果Size设置为-1,则要求传输兴趣包直到仿真结束。
8.Producer是一个处理兴趣包的应用,该应用使用指定大小以及名称前缀与兴趣包相同的数据包回应每个传入兴趣包。配置内容提供者的方法如下所示(注:截图名称有误,对象实例名称可以将comsumerHelper改为producerHelper):
9.除了转发策略,ndnSIM也支持定制内容提供者和内容请求者自身的机制。应用(Application)使用实现链路服务抽象的 AppLinkService与系统的核心进行交互。 为了简化特定NDN应用的实现,ndnSIM提供了一个基类/父类(base class) App,负责创建AppLinkService并在NDN协议栈中注册它,并为传入网络节点的兴趣包和数据包提供默认处理:
(1)定制内容请求者(Customer):以下代码显示了如何创建一个简单的ndnSIM应用,以及应用如何发送兴趣包并用数据包响应传入的兴趣包。当该consumer应用启动时,它为/ prefix / sub设置“兴趣包过滤器”(安装FIB条目),并且用这个名称前缀发送Interest。 当收到一个Interest时,会用一个1024字节的假数据包回复请求内容的兴趣包。
更多详细信息可以参阅ndnSIM和NS-3源码目录中的examples / ndn-custom-apps / custom-app.hpp,examples / ndn-custom-apps /custom-app.cpp和API文档。
头文件example/ ndn-custom-apps /custom-app.hpp:
源文件examples/ndn-custom-apps/custom-app.cpp,其中出现的AddRoute函数可以参照笔记第2点:
(注:在第69行中,对于m_appLink,它是一个类,中间这个链路连接着左右两端节点,实际上只要调用一个方法就兼顾了左右两方,对于producer来说onReceiveInterest就是从这条中间链路收到兴趣包,对于Consumer来说,onReceiveInterest就是自己发送兴趣包到这条中间链路上,是中间链路m_appLink调用的方法,而不是Consumer调用的,实质上说的是,中间链路接收到兴趣包后该怎么办。)
(2)定制内容提供者。下面的代码定制了一个producer,该Producer不会回应任何传入的兴趣包,以下是头文件examples/ndn-custom-apps/hijacker.hpp:
源文件examples/ndn-custom-apps/hijacker.cpp:
在内容请求者和内容提供者都定制完成后,在仿真场景文件ndn-simple-with-custom-app.cpp中进行配置:
在命令行中先输入cd ndnSIM/ns-3将当前所在目录移动到包含waf编译运行工具所在的文件夹目录ns-3,再使用如下命令,可以在运行仿真的时候记录CustomApp的行为记录:
10.ndnSIM支持可以进行多种记录仿真结果的多个类,例如:
(1)Packet-level trace helpers,其中包括:
a. ndn::L3RateTracer,用于跟踪节点字节速率和该节点转发的兴趣包/数据包的每秒数量
以下示例在所有仿真节点上启用这种追踪记录方式:
输出文件格式是制表符/空格分隔的值,第一行指定列的名称。有关每一列的含义,如下表所示:
b.L2Tracer,该跟踪器类似于ndn::L3RateTracer,但它仅跟踪第2层上的数据包丢包(例如,由于传输队列溢出)。以下示例在所有仿真节点上启用跟踪:
输出文件格式是制表符/空格分隔的值,第一行指定列的名称。有关每一列的含义,如下表所示:
有关使用Packet-level trace helper的例子拓扑图如下所示:
对应的拓扑文件topo-tree.txt如下所示:
仿真场景文件ndn-tree-tracers.cpp使用了trace helper,如下所示:
在命令行中先输入cd ndnSIM/ns-3将当前所在目录移动到包含waf编译运行工具所在的文件夹目录ns-3,然后在命令行中使用如下命令启动仿真场景文件:
成功运行将在运行仿真的当前目录ns-3下创建rate-trace.txt文件,可以手动分析或用作某些图形/统计数据程序的输入,例如R语言和origin。
(2)Content storetrace helper,其中ndn::CsTracer可以统计缓存命中率和缓存未命中率,使用方式如下所示:
其中Seconds()参数为记录文件的时间间隔,默认每0.5秒记录一次。输出的cs-trace.txt文件中的内容如下:
第一列是Time,即仿真产生该条数据的时刻点。
第二列是Node,即节点的id,在仿真中为唯一的。
第三列是Type,在该时刻记录的计数器的类别,分为两种:(1)CacheHits,即被本地缓存所满足的兴趣包的数量。(2)CacheMisses,即没有被本地缓存所满足的兴趣包的数量。
第四列是Packets,在该时间间隔记录到的包数量,这个数量指的是两种类别中的哪个基于第三列。
下面的例子(ndn-tree-cs-tracers.cpp)演示了内容仓库跟踪器的基本用法,但不是同时启动几个Consumer和Producer的Application,而是彼此间隔10 ms启动:
在命令行中先输入cd ndnSIM/ns-3将当前所在目录移动到包含waf编译运行工具所在的文件夹目录ns-3,可以使用如下命令启动仿真并输出统计记录文件:
成功运行将在运行仿真的当前目录ns-3下创建cs-trace.txt,可以手动分析或用作某些图/统计数据包的输入。
(3)Application-leveltrace helper,通过使用ndn::AppDelayTracer,可以获得有关发出Interest到接收相应数据包之间延迟的数据。
输出文件格式是制表符/空格分隔的值,第一行指定列的名称。有关每一列的含义,如下表所示:
下面的仿真场景文件ndn-tree-app-delay-tracer.cpp演示了Application-level trace helper的基本用法:
在命令行中先输入cd ndnSIM/ns-3将当前所在目录移动到包含waf编译运行工具所在的文件夹目录ns-3,再使用如下的命令可以运行该仿真场景:
成功运行将在运行仿真的当前目录ns-3下创建app-delays-trace.txt,可以手动分析或用作某些图形/统计数据包的输入,例如R语言或origin。
12.ndnSIM中仿真场景文件(scenario)中各项参数设置与编写的顺序(可参照上面几点的完整仿真场景文件):
(1)通过拓扑文件读取节点拓扑信息,或者手动指定节点与链路:
(2)在各节点上安装NDN协议栈(可选配置OldContentStore):
(3)在各节点上安装全局路由控制器:
(4)如果是所有节点的命名前缀和路由策略都一样,可以在(3)之前设置所有节点的路由策略:
如果需要在对应节点和名称前缀上配置路由策略,则在(3)之后,找到对应节点,在对应名称空间上安装不同的路由策略:
或者也仅在指定名称前缀上安装特定的路由策略:
(5)配置内容请求者和内容提供者的Application(应用)的属性:
(6)在全局网络拓扑上安装配置好参数的路由。如果(5)中已经在producerHelper.Install安装内容提供者应用之前配置了AddOrigins指令(如(5)所示,同时注意,AddOrigins()方法表示设置该producer可以发数据包去满足具有哪个名称前缀的兴趣包,producer的SetPrefix()方法表示自己发出的数据包的名称前缀,AddOrigins()和SetPrefix()方法中设置的两个名称前缀必须相同,如都是"/dst1",否则可能producer不会返回数据包),则直接利用CalculateRoutes()函数安装配置好参数的路由,如果在安装producer应用后没有配置AddOrigins,则可以如下配置:
(7)设置对应类别的仿真统计结果输出到哪个文件(可选),设置仿真时间(可选,不设置则会一直跑),启动仿真与停止仿真: