文章目录

  • 前言
  • 代码分析
    • 0.头文件
      • 作用
      • 详细解析
    • 1.配置网络初始参数
      • 作用
      • 详细解析
    • 2.命令行参数解析
      • 作用
    • 3.创建3个网络节点
      • 作用
      • 详细解析
    • 4.定义网络拓扑结构
      • 作用
      • 详细解析
    • 5.将所有节点添加到ndn Stack里
      • 作用
      • 详细解析
    • 6.设置转发策略
      • 作用
      • 详细解析
    • 7.Consumer设置
      • 作用
      • 详细解析
    • 8.Producer设置
      • 作用
      • 详细解析
    • 9.开始执行仿真
      • 作用
  • 如何运行代码
  • 结果分析
    • 1.准备工作和结束工作
    • 2.循环体部分
  • 结语

前言

由于我还是一名大四学生,也是一名ndnSIM的初学者,在文章中难免会出现许多错误。希望初学者看到本文时不要全盘相信,也欢迎大佬们对我的文章进行斧正!

代码分析

首先先从最简单的 ~/newndnSIM/ns-3/src/ndnSIM/examples/ndn-simple.cpp 来切入,对整个ndnSIM的框架进行分析。为了方便读者对比分析,我在这里将代码复制一份到文章中(删去了大段注释)。

// ndn-simple.cpp#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/ndnSIM-module.h"namespace ns3 {int
main(int argc, char* argv[])
{// setting default parameters for PointToPoint links and channelsConfig::SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("1Mbps"));Config::SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms"));Config::SetDefault("ns3::QueueBase::MaxSize", StringValue("20p"));// Read optional command-line parameters (e.g., enable visualizer with ./waf --run=<> --visualizeCommandLine cmd;cmd.Parse(argc, argv);// Creating nodesNodeContainer nodes;nodes.Create(3);// Connecting nodes using two linksPointToPointHelper p2p;p2p.Install(nodes.Get(0), nodes.Get(1));p2p.Install(nodes.Get(1), nodes.Get(2));// Install NDN stack on all nodesndn::StackHelper ndnHelper;ndnHelper.SetDefaultRoutes(true);ndnHelper.InstallAll();// Choosing forwarding strategyndn::StrategyChoiceHelper::InstallAll("/prefix", "/localhost/nfd/strategy/multicast");// Installing applications// Consumerndn::AppHelper consumerHelper("ns3::ndn::ConsumerCbr");// Consumer will request /prefix/0, /prefix/1, ...consumerHelper.SetPrefix("/prefix");consumerHelper.SetAttribute("Frequency", StringValue("10")); // 10 interests a secondauto apps = consumerHelper.Install(nodes.Get(0));                        // first nodeapps.Stop(Seconds(10.0)); // stop the consumer app at 10 seconds mark// Producerndn::AppHelper producerHelper("ns3::ndn::Producer");// Producer will reply to all requests starting with /prefixproducerHelper.SetPrefix("/prefix");producerHelper.SetAttribute("PayloadSize", StringValue("1024"));producerHelper.Install(nodes.Get(2)); // last nodeSimulator::Stop(Seconds(20.0));Simulator::Run();Simulator::Destroy();return 0;
}} // namespace ns3int
main(int argc, char* argv[])
{return ns3::main(argc, argv);
}

0.头文件

作用

引入头文件。前3个是ns-3的文件,最后是ndnSIM的文件。源代码如下——

#include "ns3/core-module.h"  // core模块
#include "ns3/network-module.h"  // network模块
#include "ns3/point-to-point-module.h"  //point-to-point模块
#include "ns3/ndnSIM-module.h"  // ndnSIM自己的模块

详细解析

首先先看前面 include 的文件,我在文章ndnSIM学习(二)——配置VScode的跨文件转到定义中提到,这些文件的地址是相对于 ~/newndnSIM/ns-3/build/
,里面的内容都是清一色的 #inlude

这里面include的库中,前3个都是ns-3软件的函数,最后一个才是ndnSIM自己的,里面就只有一行 #include "ndn-all.hpp" ,这里面include了ndnSIM的所有头文件。

1.配置网络初始参数

作用

配置网络的初始参数,如网络数据传输速率、信道时延、队列最大容量。源代码如下——

  // setting default parameters for PointToPoint links and channelsConfig::SetDefault("ns3::PointToPointNetDevice::DataRate", StringValue("1Mbps"));Config::SetDefault("ns3::PointToPointChannel::Delay", StringValue("10ms"));Config::SetDefault("ns3::QueueBase::MaxSize", StringValue("20p"));

详细解析

从字面意思我们就能看出, Config::SetDefault 函数的作用是配置一些网络初始参数,其作用相当于ns-3的 SetAttribute 函数。具体而言,就是配置 DataRate=1MbpsDelay=10msMaxSize=20p ,相当于——

  • 点对点数据传输速率为 106bit/s10^6\mathrm{bit/s}106bit/s (此处 1M=10^6 ,不是 1024*1024
  • 点对点路由延迟为 10ms10\mathrm{ms}10ms ,相当于每一跳到下一跳的延迟都是固定的 10ms10\mathrm{ms}10ms 。
  • 队列最多容纳 20p=20packet20\mathrm{p}=20\mathrm{packet}20p=20packet ,相当于最多有20个包。

注:

  1. 其中用到了 Config::SetDefault 函数,这是ns-3的函数,关于该函数,可以参考链接https://www.nsnam.org/doxygen/group__config.html#ga2e7882df849d8ba4aaad31c934c40c06
  2. 关于 ns3::PointToPoint 类,可以参考链接https://blog.csdn.net/loongkingwhat/article/details/53455691
  3. 关于 ns3:QueueBase 类,可以参考链接 https://www.nsnam.org/doxygen/classns3_1_1_queue_base.html。

2.命令行参数解析

作用

解析命令行参数,例如在命令行里使用 --vis 参数就可以启动可视化。源代码如下——

  // Read optional command-line parameters (e.g., enable visualizer with ./waf --run=<> --visualizeCommandLine cmd;cmd.Parse(argc, argv);

3.创建3个网络节点

作用

创建3个网络节点。源代码如下——

  // Creating nodesNodeContainer nodes;nodes.Create(3);

详细解析

看起来没什么好说了,毕竟短短两行代码,但这里涉及一些ns-3的语法需要特别指出一下。

NodeContainer 是装有一个指向 Node 类的智能指针的 vector 容器,具体而言,其中的成员为

class NodeContainer
{public:...  // 省略诸多操作函数
private:std::vector<Ptr<Node> > m_nodes;  // NodeContainer真正的数据成员
}

std::vector 大家都很熟悉,就是 vector 容器。这里值得一提的是, Ptr 类是ns-3自己实现的智能指针类1,可能是作者想要强制大家代码规范,将内存交给智能指针管理,以防内存泄漏吧。

至于 Create 函数,我们一看源代码里面的实现,马上就能明白了。传的参数 n 就代表往 vector 容器里 push_back nnn 个 Node 嘛。

// node-container.cc
NodeContainer::Create (uint32_t n)
{for (uint32_t i = 0; i < n; i++){m_nodes.push_back (CreateObject<Node> ());}
}

4.定义网络拓扑结构

作用

定义了网络的拓扑结构为 Node0->Node1->Node2 。源代码如下——

  // Connecting nodes using two linksPointToPointHelper p2p;p2p.Install(nodes.Get(0), nodes.Get(1));p2p.Install(nodes.Get(1), nodes.Get(2));

详细解析

创建了一个 PointToPointHelper 对象来协助管理整个网络,通过 point-to-pint-helper.h 文件可以看出, Install (Ptr<Node> a, Ptr<Node> b) 函数的作用在于连接两个 Node ,相当于定义拓扑关系。

node.Get(2) 操作相当于取出 node 这个容器的第2个元素,类比到数组上, node.Get(2)==node[2]

5.将所有节点添加到ndn Stack里

作用

将所有节点添加到ndn Stack里。源代码如下——

  // Install NDN stack on all nodesndn::StackHelper ndnHelper;ndnHelper.SetDefaultRoutes(true);ndnHelper.InstallAll();

详细解析

ndnHelper.SetDefaultRoutes(true) 代表FIB(Forwarding Information Base)启动默认路由, ndnHelper.InstallAll() 代表将所有节点都添加到ndn Stack里,这个 InstallAll 里面内容很多,我只能大致介绍下思路,因为我自己都快被绕晕了。

InstallAll 其实执行的是 Install(NodeContainer::GetGlobal()) ,取出了前面创建的 NodeContainer 存的全部节点,为每个节点安排 doInstall 事件,该事件将节点与 L3Protocol 聚合,并为与该节点相关的 Device 注册 Face 接口。

6.设置转发策略

作用

设置Consumer前缀为 /prefix (这一点存疑,请勿轻易相信),并设置ndn的转发策略为 multicast 。源代码如下——

  // Choosing forwarding strategyndn::StrategyChoiceHelper::InstallAll("/prefix", "/localhost/nfd/strategy/multicast");

详细解析

"/prefix" 参数好像是让Consumer的请求前缀变成 /prefix ,具体因为我 --vis 弄不出来,没法测试,(这一点存疑,请勿轻易相信)。

重点在于第二个参数 "/localhost/nfd/strategy/multicast" 上,经过我的反复测试与查找后,终于知道了:这个转发策略对应的文件是 ~/newndnSIM/ns-3/src/ndnSIM/NFD/daemon/fw/multicast-strategy.hpp~/newndnSIM/ns-3/src/ndnSIM/NFD/daemon/fw/multicast-strategy.cpp

其中 class MulticastStrategy 公有继承了 Strategy 类以及另一个类。从 class MulticastStrategy 构造函数的实现中来看,继承的 Strategy 类的构造函数传的参数正是 Forwarder 类的对象。代码胜千言,说这么多还不如来一段代码——

// multicast-strategy.hpp
class MulticastStrategy : public Strategy, public ProcessNackTraits<MulticastStrategy>
{public:explicitMulticastStrategy(Forwarder& forwarder, const Name& name = getStrategyName());...  // 省略后续代码
};// multicast-strategy.cpp
MulticastStrategy::MulticastStrategy(Forwarder& forwarder, const Name& name): Strategy(forwarder), ProcessNackTraits(this), ...  // 省略其余部分
{...  // 省略后续代码
}

也就是说,我们想要了解转发策略的代码,就应当看懂 Forwarder 类,也就是 ~/newndnSIM/ns-3/src/ndnSIM/NFD/daemon/fw/forwarder.*

7.Consumer设置

作用

设置Consumer(消费者)的信息,包括设置 TypeIdPrefixFrequency 、指定哪个节点作为Consumer、请求持续时长。源代码如下——

  // Installing applications// Consumerndn::AppHelper consumerHelper("ns3::ndn::ConsumerCbr");// Consumer will request /prefix/0, /prefix/1, ...consumerHelper.SetPrefix("/prefix");consumerHelper.SetAttribute("Frequency", StringValue("10")); // 10 interests a secondauto apps = consumerHelper.Install(nodes.Get(0));                        // first nodeapps.Stop(Seconds(10.0)); // stop the consumer app at 10 seconds mark

详细解析

ndn::AppHelper consumerHelper("ns3::ndn::ConsumerCbr");
创建 AppHelper 对象,设置 TypeId"ns3::ndn::ConsumerCbr"
consumerHelper.SetPrefix("/prefix");
设置请求前缀 Prefix/prefix
consumerHelper.SetAttribute("Frequency", StringValue("10"));
设置属性 "Frequency"10 (每秒发10个兴趣包)
auto apps = consumerHelper.Install(nodes.Get(0));
Node0 设置为Consumer(消费者)
apps.Stop(Seconds(10.0));
该消费者持续请求10s后停止

8.Producer设置

作用

设置Producer(生产者)的信息,包括设置 TypeIdPrefixPayloadSize 、指定哪个节点作为Producer。源代码如下——

  // Producerndn::AppHelper producerHelper("ns3::ndn::Producer");// Producer will reply to all requests starting with /prefixproducerHelper.SetPrefix("/prefix");producerHelper.SetAttribute("PayloadSize", StringValue("1024"));producerHelper.Install(nodes.Get(2)); // last node

详细解析

ndn::AppHelper producerHelper("ns3::ndn::Producer");
创建 AppHelper 对象,设置 TypeId"ns3::ndn::Producer"
producerHelper.SetPrefix("/prefix");
设置回应前缀 Prefix/prefix
producerHelper.SetAttribute("PayloadSize", StringValue("1024"));
设置属性 "PayloadSize"1024 (数据包大小为1024Bytes=8192bit)
producerHelper.Install(nodes.Get(2));
Node2 设置为Producer(生产者)

9.开始执行仿真

作用

设置仿真时长为20s,并执行仿真,。源代码如下——

  Simulator::Stop(Seconds(20.0));Simulator::Run();Simulator::Destroy();return 0;

如何运行代码

因为我的ndnSIM可视化没有装好, --vis 参数用不了,不能可视化分析结果。但问题不大,我们可以以日志的形式输出结果分析。根据 ndn-simple.cpp 注释的提示,我们执行如下命令行

cd ~/newndnSIM/ns-3  # 正常人的目录一般是~/ndnSIM/ns-3
NS_LOG=ndn.Consumer:ndn.Producer ./waf --run=ndn-simple  # 加上NS_LOG参数以输出日志

其中 NS_LOG=ndn.Consumer:ndn.Producer 代表我们希望程序输出 ndn.Consumerndn.Producer 的信息,所有可以被输出显示的 NDN 相关内容如下表所示——

ndn-cxx.ndn.Face=0
ndn-cxx.ndn.mgmt.Dispatcher=0
ndn-cxx.ndn.security.pib.Pib=0
ndn-cxx.ndn.security.v2.CertificateBundleFetcher=0
ndn-cxx.ndn.security.v2.CertificateCache=0
ndn-cxx.ndn.security.v2.CertificateFetcher=0
ndn-cxx.ndn.security.v2.CertificateFetcher.FromNetwork=0
ndn-cxx.ndn.security.v2.KeyChain=0
ndn-cxx.ndn.security.v2.TrustAnchorGroup=0
ndn-cxx.ndn.security.v2.ValidationState=0
ndn-cxx.ndn.security.v2.Validator=0
ndn-cxx.ndn.security.validator_config.Rule=0
ndn-cxx.nfd.AccessStrategy=0
ndn-cxx.nfd.AsfStrategy=0
ndn-cxx.nfd.BestRouteStrategy2=0
ndn-cxx.nfd.CommandAuthenticator=0
ndn-cxx.nfd.ContentStore=0
ndn-cxx.nfd.CsPolicy=0
ndn-cxx.nfd.DeadNonceList=0
ndn-cxx.nfd.FaceManager=0
ndn-cxx.nfd.FaceSystem=0
ndn-cxx.nfd.FaceTable=0
ndn-cxx.nfd.FibManager=0
ndn-cxx.nfd.FibUpdater=0
ndn-cxx.nfd.Forwarder=0
ndn-cxx.nfd.GenericLinkService=0
ndn-cxx.nfd.InternalClientTransport=0
ndn-cxx.nfd.InternalForwarderTransport=0
ndn-cxx.nfd.LinkService=0
ndn-cxx.nfd.LpFragmenter=0
ndn-cxx.nfd.LpReassembler=0
ndn-cxx.nfd.MulticastStrategy=0
ndn-cxx.nfd.NameTree=0
ndn-cxx.nfd.NameTreeHashtable=0
ndn-cxx.nfd.NameTreeIterator=0
ndn-cxx.nfd.NetdevBound=0
ndn-cxx.nfd.NfdRibReadvertiseDestination=0
ndn-cxx.nfd.NullTransport=0
ndn-cxx.nfd.PrivilegeHelper=0
ndn-cxx.nfd.ProcessNackTraits=0
ndn-cxx.nfd.RandomStrategy=0
ndn-cxx.nfd.Readvertise=0
ndn-cxx.nfd.ReadvertiseDestination=0
ndn-cxx.nfd.Rib=0
ndn-cxx.nfd.RibEntry=0
ndn-cxx.nfd.RibManager=0
ndn-cxx.nfd.RibService=0
ndn-cxx.nfd.SelfLearningStrategy=0
ndn-cxx.nfd.Strategy=0
ndn-cxx.nfd.StrategyChoice=0
ndn-cxx.nfd.StrategyChoiceManager=0
ndn-cxx.nfd.Transport=0
ndn.App=0
ndn.AppDelayTracer=0
ndn.AppHelper=0
ndn.AppLinkService=0
ndn.Consumer=all|prefix_all
ndn.ConsumerBatches=0
ndn.ConsumerCbr=0
ndn.ConsumerPcon=0
ndn.ConsumerWindow=0
ndn.ConsumerZipfMandelbrot=0
ndn.CsTracer=0
ndn.FibHelper=0
ndn.GlobalRoutingHelper=0
ndn.GlobalRoutingHelperLfid=0
ndn.L3Protocol=0
ndn.L3RateTracer=0
ndn.LinkControlHelper=0
ndn.NetDeviceTransport=0
ndn.NetworkRegionTableHelper=0
ndn.Producer=all|prefix_all
ndn.RttEstimator=0
ndn.RttMeanDeviation=0
ndn.StackHelper=0
ndn.StrategyChoiceHelper=0

结果分析

这个代码里面有一处我个人怀疑是BUG的地方,就是: ~/newndnSIM/ns-3/src/ndnSIM/apps/ndn-consumer.cppConsumer::OnData 函数和 ~/newndnSIM/ns-3/src/ndnSIM/apps/ndn-producer.cppProducer::OnInterest 函数的 NS_LOG_FUNCTION 输出居然是指针形式。

所以我个人把 NS_LOG_FUNCTION(this << data); 改成了 NS_LOG_FUNCTION(this << *data); ,把 NS_LOG_FUNCTION(this << interest); 改成了 NS_LOG_FUNCTION(this << *interest); ,这样修改后输出日志的可读性大幅提高。

完整输出很长,我们并不需要全看,因为绝大多数都是循环的。实际上,整个输出信息分为准备工作->循环部分0->循环部分1->…->循环部分99->结束程序。为了将输出日志变得更加可读,我手动加了注释分段,让程序变得更加清晰。

# 第一部分:准备工作
+0.000000000s 0 ndn.Consumer:Consumer()
+0.000000000s 2 ndn.Producer:Producer()
+0.000000000s 0 ndn.Consumer:StartApplication()
# 循环部分0:[0s, 0.1s)
+0.000000000s 0 ndn.Consumer:SendPacket()
+0.000000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.000000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +0.0ns. already 0 items
+0.000000000s 2 ndn.Producer:StartApplication()
+0.020559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FE%00?Nonce=3506978425&Lifetime=2000)
+0.020559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FE%00
+0.057663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FE%00
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.057663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 0
+0.057663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
# 循环部分1:[0.1s, 0.2s)
+0.100000000s 0 ndn.Consumer:SendPacket()
+0.100000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 1
+0.100000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 1 with +100000000.0ns. already 0 items
+0.120559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FE%01?Nonce=2606383082&Lifetime=2000)
+0.120559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FE%01
+0.157663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FE%01
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.157663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 1
+0.157663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
# 循环部分2:[0.2s, 0.3s)
# 循环部分...
# 循环部分n:[n*0.1s, n*0.1s+0.1s)
# 循环部分...
# 循环部分99:[9.9s, 10s)
+9.900000000s 0 ndn.Consumer:SendPacket()
+9.900000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 99
+9.900000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 99 with +9900000000.0ns. already 0 items
+9.920559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FEc?Nonce=1946770576&Lifetime=2000)
+9.920559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FEc
+9.957663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FEc
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+9.957663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 99
+9.957663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
# 结束程序
+10.000000000s 0 ndn.Consumer:StopApplication()

1.准备工作和结束工作

先看 0s 时的准备工作的输出,整个输出格式是 时间 对象Id 执行的函数

# 第一部分:准备工作
+0.000000000s 0 ndn.Consumer:Consumer()
+0.000000000s 2 ndn.Producer:Producer()
+0.000000000s 0 ndn.Consumer:StartApplication()
# 循环部分...
# 结束程序
+10.000000000s 0 ndn.Consumer:StopApplication()

前两行(不含注释)是定义 Node0=ConsumerNode2=Producer ,第三行是开始整个应用程序仿真,第四行是结束程序。中间则是循环部分。这就是整个运行程序的框架。

2.循环体部分

可以看到,循环部分 nnn 对应的时间片段为 [n,n+1)×0.1s\left[n,\ n+1\right)\times0.1\mathrm{s}[n, n+1)×0.1s

# 循环部分0:[0s, 0.1s)
+0.000000000s 0 ndn.Consumer:SendPacket()
+0.000000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 0
+0.000000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 0 with +0.0ns. already 0 items
+0.000000000s 2 ndn.Producer:StartApplication()
+0.020559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FE%00?Nonce=3506978425&Lifetime=2000)
+0.020559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FE%00
+0.057663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FE%00
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.057663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 0
+0.057663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
# 循环部分1:[0.1s, 0.2s)
+0.100000000s 0 ndn.Consumer:SendPacket()
+0.100000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 1
+0.100000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 1 with +100000000.0ns. already 0 items
+0.120559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FE%01?Nonce=2606383082&Lifetime=2000)
+0.120559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FE%01
+0.157663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FE%01
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+0.157663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 1
+0.157663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2
# 循环部分2:[0.2s, 0.3s)
# 循环部分...
# 循环部分n:[n*0.1s, n*0.1s+0.1s)
# 循环部分...
# 循环部分99:[9.9s, 10s)
+9.900000000s 0 ndn.Consumer:SendPacket()
+9.900000000s 0 ndn.Consumer:SendPacket(): [INFO ] > Interest for 99
+9.900000000s 0 ndn.Consumer:WillSendOutInterest(): [DEBUG] Trying to add 99 with +9900000000.0ns. already 0 items
+9.920559998s 2 ndn.Producer:OnInterest(0x55f912302e00, /prefix/%FEc?Nonce=1946770576&Lifetime=2000)
+9.920559998s 2 ndn.Producer:OnInterest(): [INFO ] node(2) responding with Data: /prefix/%FEc
+9.957663998s 0 ndn.Consumer:OnData(0x55f91224d030, Name: /prefix/%FEc
MetaInfo: ContentType: 0
Content: (size: 1024)
Signature: (type: Unknown(255), value_length: 1)
)
+9.957663998s 0 ndn.Consumer:OnData(): [INFO ] < DATA for 99
+9.957663998s 0 ndn.Consumer:OnData(): [DEBUG] Hop count: 2

兴趣包经过2跳路由到达生产者处,用时 20.56ms ,相当于每一跳都是 10.28ms 。考虑到固定时延为 10ms ,数据速率为 Mbps ,说明兴趣包传输的信息量为 0.28ms*1Mbps=35Byte

到达其中 Interest0Name = "/prefix/%FE%00" (共 2+(2+6 + 2+2)byte ,其中是 2 是对应地 Type+Length ,外层是一个 Interest 标志,内层是两个 GenericNameComponent 标志),随机数 Nonce 是32bits的数(共 2+4byte ),生存时间 Lifetime=2000 (共 2+2byte ),报头tlv 2byte ,一共有 26byte 。那么消失的 9byte 去哪里了呢?我也不知道,懒得测了,有空再说吧。有可能在数据链路层打了 9byte 的标签之类的。

数据包每跳时延则是 (57.664 - 20.56)/2=19.052ms ,其中减去固定的 10ms 后,算出数据长度为 8.552ms*1Mbps=1069byte ,其中 SignatureValue 里占了 2+1byteSignatureInfo 里占了 2+3byteContent 里占了 4+1024byteMetaInfo 里占了 2byteName 里占了 2+12byte ,报头tlv 4byte ,一共有 1056byte 。那么消失的 13byte 去哪里了呢?我也不知道,懒得测了,有空再说吧。有可能在数据链路层打了 13byte 的标签之类的。

顺带一提测试方法,添加头文件 #include "ns3/core-module.h" ,然后在你想要显示的地方敲一个 NS_LOG_UNCOND("Total length=" << totalLength);

结语

在本篇文章中,我们剖析了整个 ndn-simple.cpp 的代码。我们分析得十分细致,以至于几乎每一行代码都进行了深入地理解。

乍一看,这篇文章十分冗长,仅仅是一个 Hello world! 级别的程序,都花了这么大的功夫区剖析,然而,实际上,ndnSIM的代码的骨架正是由这些简单的部分组合而成,仅仅是在骨架上增添了一些更多的细节罢了

实话实说,写这篇文章花了我整整4个小时(该时间不含结果分析,那是另外的价钱.jpg),包括查阅源码,查阅链接,查阅书籍。然而,写完后再看相似的 ndn-grid.cpp 代码,就能做到一目十行,只需要一分钟就能理解到整个代码在做怎样的事情了

所谓磨刀不误砍柴功,或许聪明的读者只需要几分钟就能看完我这篇文章。当然,这也是我写本文的目的,希望读者能花更少的时间就能入门整个框架。但还是希望读者能稍微多花一点点时间,把原理搞得更透彻一点,就可以节省许多重复性的工作了。


  1. 周迪之. 开源网络模拟器ns-3架构与实践[M]. 北京:机械工业出版社, 2019 :65-70. ↩︎

ndnSIM学习(四)——examples之ndn-simple.cpp超详细剖析相关推荐

  1. 学习Nginx,看完这篇超详细的文章就够了

    目录 本文简介 一.Nginx的基本概念 1.1.Nginx是什么? 1.2.Nginx能帮助我们做些什么? 1.3.Nginx的特性 二.Nginx的安装 2.1.环境介绍 2.2.安装Nginx ...

  2. python学习笔记之numpy库的使用——超详细

    文章目录 NumPy介绍 一.如何创建 1.导入Numpy库,并命名为np: 2.查看版本 3.通过列表创建一维数组: 4.通过列表创建二维数组: 5.创建全为0的二维数组 6.创建全为1的三维数组 ...

  3. 数据结构学习笔记——顺序表的基本操作(超详细最终版+++)建议反复看看ヾ(≧▽≦*)o

    目录 前言 一.顺序表的定义 二.顺序表的初始化 三.顺序表的建立 四.顺序表的输出 五.顺序表的逆序输出 六.顺序表的插入操作 七.顺序表的删除操作 八.顺序表的按位和按值查找 基本操作的完整代码 ...

  4. 三次握手和四次挥手知识总结(超详细)

    前言: 最近学习了计算机网络的知识,看了很多的视频,并参考了很多资料,写下了这将近4500字的与"三次握手和四次挥手"相关的知识,希望能帮助到各位小伙伴儿以及加深自己印象,方便以后 ...

  5. 计算机网络学习笔记第一章(概述) 超详细整理

    计算机网络笔记整理 笔记根据B站 计算机网络微课堂(有字幕无背景音乐版)(陆续更新中-) 视频整理 原笔记链接 计算机网络,在原笔记的基础上添加了个人的部分理解,十分感谢这位同学. 目录 图示说明 1 ...

  6. 【深度学习】Batch Normalization(BN)超详细解析

    单层视角 神经网络可以看成是上图形式,对于中间的某一层,其前面的层可以看成是对输入的处理,后面的层可以看成是损失函数.一次反向传播过程会同时更新所有层的权重W1,W2,-,WL,前面层权重的更新会改变 ...

  7. Linux学习教程,Linux入门教程(超详细)| 网址推荐

    今天发现一个Linux中文教程,再此收藏备份. 目录 第1章 Linux简介 第2章 Linux安装 第3章 Linux文件和目录管理 第4章 Linux打包(归档)和压缩 第5章 Vim文本编辑器 ...

  8. 学习笔记:运算放大器(OPA)超详细讲解(内含运用电路仿真)

    一运算放大器的简介 放大器(简称"运放")是具有很高放大倍数的电路单元.运算放大器是一个内含多级放大电路的电子集成电路.分别是输入级,中间级,发大级还有偏置电路. 红色绿色蓝色分别 ...

  9. Unity学习制作Flappy Bird小游戏(超详细全教程,可发布到PC、Web、Android平台)

    本文中Flappy Bird基于Unity2019.4.7f1完成,源工程已部分代码改为适配安卓 flappy bird:一夜爆红的胖鸟 这是一款简单又困难的手机游戏,游戏中玩家必须控制一只胖乎乎的小 ...

最新文章

  1. 测试类异常Manual close is not allowed over a Spring managed SqlSession
  2. Windows进程与线程学习笔记(三)—— KPCR
  3. django.db.utils.DataError: (1406, Data too long for column 'gender' at row 1)
  4. c++ log函数_高斯拉普拉斯算子(Laplacian of Gaussian, LoG)
  5. java struts2下载zip_Struts2多文件下载
  6. CMake-add_executable()
  7. 针对建筑设计开发的CAD软件——ARCHICAD 24 for mac
  8. 详解LCD12864显示屏的使用(并行控制)
  9. hough变换检测圆周_hough变换是如何检测出直线和圆的?
  10. 使用Zip命令进行本地提权(类Unix系统)
  11. 探讨大数据时代如何规划智慧城市
  12. Android 监听wifi热点打开和关闭广播
  13. 用Xposed框架拦截微信、人人、QQ等LBS应用的当前位置
  14. Elastic: ILM与rollover的关系
  15. Android 一个简单手机响铃功能实现
  16. SQL Server 2008 R2 安装 (转)
  17. 一个画板十年工程师的PCB设计经验分享
  18. Android 使用ADB命令安装、卸载软件
  19. No module named ‘tf_slim‘解决办法
  20. CREO1——CREO 2.0画沉头孔

热门文章

  1. MPI多进程问题记录
  2. 使用晨曦记账本,记录家庭财政收支明细
  3. node.js批量修改图片名称
  4. SpringBoot整合阿里云视频点播
  5. C语言循环之空心梯形,循环-空心梯形
  6. 计算机内存占用过高怎么办,电脑内存占用过高怎么办?
  7. 使用fsck命令检查并修复linux文件系统
  8. Dataframe中筛选出满足条件的行
  9. Chart.js使用及ajax获取数据
  10. kali 2.0 安装搜狗输入法 troubleshooting