ndnSIM的使用教程

目录

一、安装

二、使用

2.1 ndnSIM模拟程序

2.2 ndnSIM模拟程序目录

2.3 仿真程序分析

2.3.1 画拓扑图  

2.3.2 配路由

2.3.3 安装程序

2.3.4 写自己的APP

三、进阶


一、安装

1、建议使用ubuntu20.04桌面版安装,因为桌面版安装出来还可以看到python生成的拓扑图。

2、安装教程请求参考官方网站的安装连接。

https://ndnsim.net/current/getting-started.html

3、下载源码的目录结构

ndnSIM的使用教程

4、判断是否安装成功,进入上图所示的ns-3目录下,运行以下命令

$ ./waf --run=ndn-simple --vis

出现如下所示的拓扑图:

ndnSIM的使用教程

点击图中的 "Simulate (F3)" 可以看到拓扑变绿色,数据正常传输,就表示安装成功了。

ndnSIM的使用教程

二、使用

2.1 ndnSIM模拟程序

    ndnSIM的模拟程序的代码逻辑一般有3部分组成。

    (1)画拓扑;

    (2)配路由;

    (3)在拓扑图中选两个节点,一个安装发包程序(consumer)用于发兴趣包,一个安装收包和发送数据包的程序(producer)。

2.2 ndnSIM模拟程序目录

    如下图所示,ndnSIM的所有仿真模拟程序都放在  ns-3/scratch 目录下,每个程序必须以 .cc 结尾。

ndnSIM的使用教程

    图中,作者自己创建了 wgh0,wgh1,wgh2,...等多个仿真程序,可以在ns-3目录下通过以下命令运行 wgh1.cc 这个仿真程序

$ ./waf --run=wgh1 --vis

    读者可以直接复制ns-3/scratch 目录下 ndn-simple.cc 文件到相同目录下的  wgh1.cc 文件, 然后使用以上命令运行 wgh1.cc 这个仿真程序,这就相当与读者自己写了个仿真程序!!!

2.3 仿真程序分析

    本节直接讲一种比较方便通用的仿真程序写法,不使用ndn-simple.cc为例,而是使用作者自己写的仿真代码为例。

#include "helper/ndn-app-helper.hpp"
#include "helper/ndn-fib-helper.hpp"
#include "helper/ndn-stack-helper.hpp"
#include "ns3/core-module.h"
#include "ns3/network-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/ndnSIM-module.h"
#include "utils/tracers/ndn-app-delay-tracer.hpp"

namespace ns3 {

int main(int argc, char* argv[])
{
	CommandLine cmd;
	cmd.Parse(argc, argv);

	// 25 拓扑图的大小, 画拓扑图
	AnnotatedTopologyReader topologyReader("", 25);
	topologyReader.SetFileName("scratch/wgh1-tp.txt");
	topologyReader.Read();

	ndn::StackHelper ndnHelper ;
	ndnHelper.InstallAll() ;

	ndn::StrategyChoiceHelper::InstallAll("/", "/localhost/nfd/strategy/best-route");


	ndn::FibHelper ndnFibHelper ;
	Ptr<Node> node0 = Names::Find<Node>("Node0");
	Ptr<Node> node1 = Names::Find<Node>("Node1");
	Ptr<Node> node2 = Names::Find<Node>("Node2");
	Ptr<Node> node3 = Names::Find<Node>("Node3");
	Ptr<Node> node4 = Names::Find<Node>("Node4");
	Ptr<Node> node5 = Names::Find<Node>("Node5");
	Ptr<Node> node6 = Names::Find<Node>("Node6");
	Ptr<Node> node7 = Names::Find<Node>("Node7");
	Ptr<Node> node8 = Names::Find<Node>("Node8");
	// 添加路由
	std::string prefix = "/hello";
	ndnFibHelper.AddRoute(node0, prefix, node1, 25) ;
	ndnFibHelper.AddRoute(node1, prefix, node4, 25) ;
	ndnFibHelper.AddRoute(node4, prefix, node5, 25) ;
	ndnFibHelper.AddRoute(node5, prefix, node8, 25) ;

	// 选两个点安装 consumer 和  producer
	// Getting containers for the consumer/producer
	Ptr<Node> producerNode = Names::Find<Node>("Node8");
	Ptr<Node> consumerNode = Names::Find<Node>("Node0");
	ndn::AppHelper consumerHelper("Wgh0App");
	consumerHelper.Install(consumerNode);
	ndn::AppHelper producerHelper("Wgh0Pro");
	producerHelper.Install(producerNode);
	// ---------------------------------------- install app

	Simulator::Stop(Seconds(20.0));

	Simulator::Run();
	Simulator::Destroy();

	return 0;
}

} // namespace ns3

int main(int argc, char* argv[])
{
	return ns3::main(argc, argv);
}

2.3.1 画拓扑图  

  可以看到,画拓扑图的代码只有三行。

// 25 拓扑图的大小, 画拓扑图
AnnotatedTopologyReader topologyReader("", 25);
// 25 是拓扑图大小,拓扑图是画在一个正方形网格图上的,25是网络图面积,横坐标5个格,纵坐标5个格
topologyReader.SetFileName("scratch/wgh1-tp.txt");
// 读出当前目录下的画图文件“wgh1-tp.txt”
topologyReader.Read();

    我们来看看"wgh1-tp.txt"的内容,文件前半段 route 区域是设置每个路由在网格图中的位置,link区域的配置路由器之间的连接。

router

# node  comment     yPos    xPos
Node0   NA          3       1
Node1   NA          3       2
Node2   NA          3       3
Node3   NA          2       1
Node4   NA          2       2
Node5   NA          2       3
Node6   NA          1       1
Node7   NA          1       2
Node8   NA          1       3

link

# Each line should be in the following format (only first two are required, the rest can be omitted)
# srcNode   dstNode     bandwidth   metric  delay   queue
# bandwidth: link bandwidth
# metric: routing metric
# delay:  link delay
# queue:  MaxPackets for transmission queue on the link (both directions)
Node0       Node1       1Mbps       1       10ms    10
Node0       Node3       1Mbps       1       10ms    10
Node1       Node2       1Mbps       1       10ms    10
Node1       Node4       1Mbps       1       10ms    10
Node2       Node5       1Mbps       1       10ms    10
Node3       Node4       1Mbps       1       10ms    10
Node3       Node6       1Mbps       1       10ms    10
Node4       Node5       1Mbps       1       10ms    10
Node4       Node7       1Mbps       1       10ms    10
Node5       Node8       1Mbps       1       10ms    10
Node6       Node7       1Mbps       1       10ms    10
Node7       Node8       1Mbps       1       10ms    10

    我们看看画出来是什么效果。

ndnSIM的使用教程

2.3.2 配路由


	ndn::FibHelper ndnFibHelper ;
	Ptr<Node> node0 = Names::Find<Node>("Node0");
	Ptr<Node> node1 = Names::Find<Node>("Node1");
	Ptr<Node> node2 = Names::Find<Node>("Node2");
	Ptr<Node> node3 = Names::Find<Node>("Node3");
	Ptr<Node> node4 = Names::Find<Node>("Node4");
	Ptr<Node> node5 = Names::Find<Node>("Node5");
	Ptr<Node> node6 = Names::Find<Node>("Node6");
	Ptr<Node> node7 = Names::Find<Node>("Node7");
	Ptr<Node> node8 = Names::Find<Node>("Node8");
	// 添加路由
	std::string prefix = "/hello";
	ndnFibHelper.AddRoute(node0, prefix, node1, 25) ;
    // 给node0节点添加一条路由, "/hello" 下一跳指向 node1
	ndnFibHelper.AddRoute(node1, prefix, node4, 25) ;
	ndnFibHelper.AddRoute(node4, prefix, node5, 25) ;
	ndnFibHelper.AddRoute(node5, prefix, node8, 25) ;

    也可以使用全局路由算法给整个网络配置某条最佳路由,但作者觉得那样不太灵活,所以建议这样手工配。如果需要使用全局路由算法,读者可以参考代码https://ndnsim.net/current/examples.html

2.3.3 安装程序

	// 选两个点安装 consumer 和  producer
	// Getting containers for the consumer/producer
	Ptr<Node> producerNode = Names::Find<Node>("Node8");
	Ptr<Node> consumerNode = Names::Find<Node>("Node0");
	ndn::AppHelper consumerHelper("Wgh0App");
	consumerHelper.Install(consumerNode);
	ndn::AppHelper producerHelper("Wgh0Pro");
	producerHelper.Install(producerNode);
	// ---------------------------------------- install app

    以上4行代码的意思就是在consumerNode 这个(Node0)节点安装 Wgh0App 这个程序, 在 producerNode这个节点(Node8)安装 Wgh0Pro 这个程序。

2.3.4 写自己的APP

    Wgh0App和 Wgh0Pro ,是作者自己写的一个发兴趣包的程序和一个收兴趣包的应用程序。这两个程序放在“ns-3/src/ndnSIM/apps”目录下,如下图中的 wgh0-app.hpp, wgh0-app.cpp, wgh0-pro.hpp, wgh0-pro.cpp这4个文件。

ndnSIM的使用教程

    读者可以直接将这4个文件复制到“ns-3/src/ndnSIM/apps”目录下,就可以直接在模拟仿真程序里用了。这四个程序的代码,首先是发包程序的.hpp和.cpp代码

// wgh0-app.hpp
#ifndef WGH0_APP_H_
#define WGH0_APP_H_

#include "ns3/ndnSIM/apps/ndn-app.hpp"

namespace ns3 {

class Wgh0App : public ndn::App {
public:
  static TypeId
  GetTypeId();

  virtual void
  StartApplication();

  virtual void
  StopApplication();

  virtual void
  OnData(std::shared_ptr<const ndn::Data> contentObject);

private:
  void SendInterest();
  static void sendThread(Wgh0App *_this) ;
};

} // namespace ns3

#endif // CUSTOM_APP_H_

      读者不用理会.hpp文件,应该什么都不用改。都用以上的模板就行了,可以改类名,改了类名就相当于改了APP名称,在仿真程序中安装时,传入的就是类名。 

// wgh0-app.cpp
#include <iostream>
#include <thread>
#include "model/ndn-common.hpp"
#include "wgh0-app.hpp"

#include "ns3/ptr.h"
#include "ns3/log.h"
#include "ns3/simulator.h"
#include "ns3/packet.h"
#include "ns3/ndnSIM/helper/ndn-stack-helper.hpp"
#include "ns3/ndnSIM/helper/ndn-fib-helper.hpp"
#include "ns3/random-variable-stream.h"
#include "ns3/ndnSIM/ndn-cxx/lp/MultiIdentifierHeader.hpp"
#include "ns3/ndnSIM/ndn-cxx/lp/tags.hpp"


NS_LOG_COMPONENT_DEFINE("Wgh0App");

namespace ns3{
NS_OBJECT_ENSURE_REGISTERED(Wgh0App);

// 根据类名对下面的代码做小修改
TypeId Wgh0App::GetTypeId(){
	static TypeId tid = TypeId("Wgh0App").SetParent<ndn::App>().AddConstructor<Wgh0App>();
	return tid;
}

// 作者自己写了个隔50ms发一个兴趣包的线程
void Wgh0App::sendThread(Wgh0App *_this){

	while (true) {
		Simulator::Schedule(Seconds(1.0), &Wgh0App::SendInterest, _this);
		usleep(50000) ;
	}
}

// 这个函数会被ns-3调用一次来启动我们写的代码,因此我们在这个函数里启动发包线程
void Wgh0App::StartApplication(){
	ndn::App::StartApplication();
	//Simulator::Schedule(Seconds(1.0), &Wgh0App::SendInterest, this);
	std::thread t(sendThread, this);
	t.detach();
}

// 不用改这个函数
void Wgh0App::StopApplication(){
	ndn::App::StopApplication();
}

// 发兴趣包的函数,直接在下面的基础上改改兴趣包的名称应该就满足好多需求了。
// 读者可以自己构造兴趣包,并调用 
// m_transmittedInterests(interestPtr, this, m_face);和 m_appLink->onReceiveInterest(*interestPtr); 就能把兴趣包发出去了
void Wgh0App::SendInterest(){
	static uint64_t interestId = 0 ;
	//while (true) {
	ndn::Name interestName("/hello");
	interestName.appendSequenceNumber(interestId ++) ;
	auto interestPtr = std::make_shared<ndn::Interest>(interestName);

	Ptr<UniformRandomVariable> rand = CreateObject<UniformRandomVariable>();
	interestPtr->setNonce(rand->GetValue(0, std::numeric_limits<uint32_t>::max()));

	interestPtr->setInterestLifetime(ndn::time::seconds(2));

	std::cout << "send interest : " << *interestPtr << std::endl;
	m_transmittedInterests(interestPtr, this, m_face);

	m_appLink->onReceiveInterest(*interestPtr);
	//sleep(1) ;
	//}
}

// 收到数据包的处理函数,读者可以按自己的需要修改
void Wgh0App::OnData(std::shared_ptr<const ndn::Data> data){
	std::cout << "DATA received for name " << data->getName() << std::endl;
}

}

    接下来是收兴趣包并发出数据包的代码,以下2个.hpp和.cpp文件 

// wgh0-pro.hpp
#ifndef WGH0PRO_H_
#define WGH0PRO_H_

#include "ns3/ndnSIM/apps/ndn-app.hpp"

namespace ns3 {

class Wgh0Pro : public ndn::App {
public:
  static TypeId
  GetTypeId();

  Wgh0Pro();

  // Receive all Interests but do nothing in response
  void
  OnInterest(std::shared_ptr<const ndn::Interest> interest);

protected:
  // inherited from Application base class.
  virtual void
  StartApplication();

  virtual void
  StopApplication();
};

} // namespace ns3

#endif 

     同样不会对.hpp做什么修改。

#include "wgh0-pro.hpp"

#include "model/ndn-common.hpp"
#include "ns3/log.h"

#include "ns3/ndnSIM/helper/ndn-fib-helper.hpp"
#include <memory>
#include "ns3/ndnSIM/ndn-cxx/lp/MultiIdentifierHeader.hpp"
#include "ns3/ndnSIM/ndn-cxx/lp/tags.hpp"

NS_LOG_COMPONENT_DEFINE("Wgh0Pro");

namespace ns3 {

// Necessary if you are planning to use ndn::AppHelper
NS_OBJECT_ENSURE_REGISTERED(Wgh0Pro);

TypeId
Wgh0Pro::GetTypeId()
{
  static TypeId tid = TypeId("Wgh0Pro").SetParent<ndn::App>().AddConstructor<Wgh0Pro>();

  return tid;
}

Wgh0Pro::Wgh0Pro()
{
}

// 收到兴趣包的响应函数,读者可以按自己的需要返回数据包,并调用 
// m_transmittedDatas(data, this, m_face); 和 m_appLink->onReceiveData(*data);
// 把数据包发出去就行了
void Wgh0Pro::OnInterest(std::shared_ptr<const ndn::Interest> interest)
{
	ndn::App::OnInterest(interest); // forward call to perform app-level tracing
	// do nothing else (hijack interest)
	//
	std::cout << "Receive interest : " << *interest << std::endl;

	std::string content = "hello" ;
	std::shared_ptr<ndn::Data> data = std::make_shared<ndn::Data>();
	data->setName(interest->getName());
	data->setContent((const uint8_t*)content.data(), content.size()) ;

	ndn::KeyChain keyChain ;
	keyChain.sign(*data);

	m_transmittedDatas(data, this, m_face);
	m_appLink->onReceiveData(*data);

	NS_LOG_DEBUG("Do nothing for incoming interest for" << interest->getName());
}

	void
Wgh0Pro::StartApplication()
{
	App::StartApplication();

	// equivalent to setting interest filter for "/prefix" prefix
	ndn::FibHelper::AddRoute(GetNode(), "/hello", m_face, 0);
}

	void
Wgh0Pro::StopApplication()
{
	App::StopApplication();
}

} // namespace ns3

三、进阶

    如果读者读需要改NDN的源代码,直接改 ns-3/src/ndnSIM 目录下的 ndn-cxx代码和 NFD代码即可。改完后直接使用 ./waf --run=<你的仿真程序名> --vis  这个命令来运行仿真程序就可以了。

上一篇:【网络仿真】ns3-gym/rl-tcp


下一篇:NS3-命令行参数