本文首先介绍ndnSIM提供的不同用户类的参数设置与使用方式,然后介绍用户类的核心方法SendPacket()
以及如何初始化自定义的兴趣包标签,最后介绍如何创建与使用自定义的用户类。
版本信息如下:
操作系统:Ubuntu 16.04
ndnSIM:ndnSIM-2.1
ns-3-dev:ns-3.23-dev-ndnSIM-2.1
用户类用于生成兴趣包,从而驱动整个仿真程序。各个用户类的继承关系及特点:
类名 | 继承关系 | 特点 |
---|---|---|
Consumer | 用户类的基类,继承于APP类 | 发出兴趣包 |
ConsumerCbr | 继承于Consumer | 按一定间隔发出兴趣包 |
ConsumerZipfMandelbrot | 继承于ConsumerCbr | 按Zipf-Mandelbrot分布发出兴趣包 |
ConsumerWindow | 继承于Consumer | 基于滑动窗口发出兴趣包 |
ConsumerBatches | 继承于Consumer | 在指定时间发出一定数量的兴趣包 |
⭐️在仿真实验中最常使用的是ConsumerZipfMandelbrot。
Consumer
Consumer类是所有用户类的基类,用于发出兴趣包。参数设置中我们仅介绍常用的参数。
路径:/ndnSIM/apps
设置参数
- Prefix:请求内容的前缀。
consumerHelper.SetPrefix("/prefix")
// 或 consumerHelper.SetAttribute("Prefix", StringValue("/prefix"));
- StartSeq:请求内容初始序号。
consumerHelper.SetAttribute("StartSeq", IntegerValue(0));
- LifeTime:兴趣包的生存时间(超过此时间未被响应的兴趣包将被丢弃或重发)。
consumerHelper.SetAttribute("LifeTime", StringValue("2s"));
其他用户类中也可设置以上参数。
使用
ndn::AppHelper consumerHelper("ns3::ndn::Consumer");
consumerHelper.SetAttribute(...);
consumerHelper.Install(consumerNodes); // 在指定节点上安装用户
ConsumerCbr
ConsumerCbr类按一定时间间隔发出不重复的兴趣包,所以若实验中仅有一个用户,那么缓存中的内容永远不会被响应。
设置参数
- Frequency:发出兴趣包的频率。
consumerHelper.SetAttribute("Frequency", StringValue("1.0"));
- Randomize:发出间隔的分布方式,可选值有:
"none"
:间隔时间为常数,即速率为固定值;"uniform"
:间隔时间为(0,1/Frequency)的均匀分布;"exponential"
:间隔时间为均值是1/Frequency的指数分布。
consumerHelper.SetAttribute("Randomize", StringValue("none")); // 或"uniform"或"exponential"
- MaxSeq:请求内容的最大序号,超过该最大序号后用户将不再发出兴趣包。
consumerHelper.SetAttribute("MaxSeq", IntegerValue(std::numeric_limits<uint32_t>::max())); // 默认为整型的最大值
使用
ndn::AppHelper consumerHelper("ns3::ndn::ConsumerCbr");
consumerHelper.SetAttribute(...);
consumerHelper.Install(consumerNodes); // 在指定节点上安装用户
ConsumerZipfMandelbrot
ConsumerZipfMandelbrot类继承于ConsumerCbr类,按Zipf-Mandelbrot分布发出兴趣包,这是ndnSIM仿真实验中最常使用的用户类。
设置参数
- Frequency:发出兴趣包的频率,同ConsumerCbr类。
consumerHelper.SetAttribute("Frequency", StringValue("1.0"));
- NumberOfContents:内容总数。
consumerHelper.SetAttribute("NumberOfContents", StringValue("100"));
- q:用以控制峰值的左右偏移,一般设置为0。
consumerHelper.SetAttribute("q", StringValue("0"));
- s:Zipf参数,用以控制流行内容的集中程度,一般取$[0.2,1.5]$。
consumerHelper.SetAttribute("s", StringValue("0.7"));
使用
ndn::AppHelper consumerHelper("ns3::ndn::ConsumerZipfMandelbrot");
consumerHelper.SetAttribute(...);
consumerHelper.Install(consumerNodes); // 在指定节点上安装用户
ConsumerWindow
ConsumerWindow类是一个基于滑动窗口的用户类,可发出变化速率的兴趣包。
设置参数
- Window:初始窗口大小,即无需等待数据包返回就能发送出的初始兴趣包数(未被响应的兴趣包数)。
consumerHelper.SetAttribute("Window", StringValue("1"));
- PayloadSize:数据包的平均大小。
consumerHelper.SetAttribute("PayloadSize", UintegerValue(1040));
- Size:请求的数据总量,单位MB,当总量达到设置值时用户不再发出兴趣包。默认为-1,表示不限制数据总量。
consumerHelper.SetAttribute("Size", DoubleValue(-1));
- MaxSeq:请求内容的最大序号,超过该最大序号后用户将不再发出兴趣包。只有Size为-1时MaxSeq才被激活为可进行设置,否则设置无效。
consumerHelper.SetAttribute("MaxSeq", IntegerValue(std::numeric_limits<uint32_t>::max());
使用
ndn::AppHelper consumerHelper("ns3::ndn::ConsumerWindow");
consumerHelper.SetAttribute(...);
consumerHelper.Install(consumerNodes); // 在指定节点上安装用户
ConsumerBatches
ConsumerBatches类是一个基于滑动窗口的用户类,可发出变化速率的兴趣包。
设置参数
- Batches:参数值为向量,包括时间与数量的组合,用以指定用户在何时发出多少兴趣包。
// 用户在1s时发出1个兴趣包,在2s时发出5个兴趣包
consumerHelper.SetAttribute("Window", StringValue("1s 1 2s 5"));
使用
ndn::AppHelper consumerHelper("ns3::ndn::ConsumerWindow");
consumerHelper.SetAttribute(...);
consumerHelper.Install(consumerNodes); // 在指定节点上安装用户
发出兴趣包SendPacket()
用户类中最重要的方法是SendPacket()
,用以创建并按照一定规则发出兴趣包。我们以基类Consumer类为例,详细介绍SendPacket()
方法。
void
Consumer::SendPacket()
{
if (!m_active)
return;
NS_LOG_FUNCTION_NOARGS();
/********************(1)创建兴趣包的序号********************/
uint32_t seq = std::numeric_limits<uint32_t>::max(); // 定义兴趣包序号
while (m_retxSeqs.size()) {
seq = *m_retxSeqs.begin(); // 设置当前兴趣包序号为序号数组的第一位
m_retxSeqs.erase(m_retxSeqs.begin()); // 删除序号数组的第一位
break;
}
if (seq == std::numeric_limits<uint32_t>::max()) {
if (m_seqMax != std::numeric_limits<uint32_t>::max()) {
if (m_seq >= m_seqMax) {
return; // 发完所有兴趣包
}
}
seq = m_seq++;
}
// 将当前兴趣包序号附加到兴趣包名称前缀中,构成总的兴趣包名称
shared_ptr<Name> nameWithSequence = make_shared<Name>(m_interestName);
nameWithSequence->appendSequenceNumber(seq);
/********************(1)END*******************/
/********************(2)创建兴趣包并设置参数********************/
// 创建兴趣包
shared_ptr<Interest> interest = make_shared<Interest>();
// 设置兴趣包Nonce值,用以避免兴趣包在网络各节点之间循环转发
interest->setNonce(m_rand->GetValue(0, std::numeric_limits<uint32_t>::max()));
// 设置兴趣包的名称
interest->setName(*nameWithSequence);
// 设置兴趣包的生存时间
time::milliseconds interestLifeTime(m_interestLifeTime.GetMilliSeconds());
interest->setInterestLifetime(interestLifeTime);
// 此处可设置自定义的参数或标签,详见“ndnSIM仿真平台使用之在兴趣包与数据包内添加标签(字段)”
interest->setHops(0);
interest->setFaces("");
// NS_LOG_INFO ("Requesting Interest: \n" << *interest);
NS_LOG_INFO("> Interest for " << seq);
/********************(2)END*******************/
/********************(3)发出兴趣包********************/
WillSendOutInterest(seq);
m_transmittedInterests(interest, this, m_face);
m_face->onReceiveInterest(*interest);
// 按一定规则发出下个兴趣包,如指数分布、均匀分布、Zipf-Mandelbrot分布等
ScheduleNextPacket();
/********************(3)END*******************/
}
创建与使用自定义的用户类
在大部分的仿真实验中并不需要对ndnSIM提供的用户类做过多修改,只需要在参数设置部分为自定义的兴趣包标签设置初始值。若直接修改原有用户类的代码,会影响原有用户类的使用(尽管大部分情况下这种方式非常方便),因此我们推荐创建一个自定义的用户类用于我们自己的仿真实验。
为此,我们创建一个ConsumerZipfMandelbrotTest类,该类与原ConsumerZipfMandelbrot类几乎一致,仅在加入一个自定义的整型标签TestTag(如何添加标签见ndnSIM仿真平台使用之在兴趣包与数据包内添加标签(字段))。此外,考虑到如果每次实验中标签TagTest有不同的初始化值,那么每次都在SendPacket()
重新赋值该标签将大大影响实验的效率(因为ndnSIM需要重新编译该类),因此我们将在示例文件中显式地设置标签的初始化值。
需要注意的是,显式地在示例文件中对用户类中的某个变量赋值时,该变量不仅可以是兴趣包的标签,也可以是其他与实验相关的变量。
创建类所在文件
复制ndn-consumer-zipf-mandelbrot.hpp与ndn-consumer-zipf-mandelbrot.cpp,并分别命名为ndn-consumer-zipf-mandelbrot-test.hpp与ndn-consumer-zipf-mandelbrot-test.cpp。
修改类名
- 将两个文件的代码中所有的
ConsumerZipfMandelbrot
替换为ConsumerZipfMandelbrotTest
:
NS_LOG_COMPONENT_DEFINE("ndn.ConsumerZipfMandelbrotTest");
namespace ns3 {
namespace ndn {
NS_OBJECT_ENSURE_REGISTERED(ConsumerZipfMandelbrotTest);
TypeId
ConsumerZipfMandelbrotTest::GetTypeId(void){
...
}
...
- 将ndn-consumer-zipf-mandelbrot-test.cpp中的include文件ndn-consumer-zipf-mandelbrot.hpp修改为ndn-consumer-zipf-mandelbrot-test.hpp:
#include "ndn-consumer-zipf-mandelbrot-test.hpp"
添加自定义标签设置接口
- 在ndn-consumer-zipf-mandelbrot-test.hpp中的ConsumerZipfMandelbrot类定义里添加私有属性m_test_tag:
class ConsumerZipfMandelbrotTest : public ConsumerCbr {
...
private:
uint32_t m_N; // number of the contents
double m_q; // q in (k+q)^s
double m_s; // s in (k+q)^s
std::vector<double> m_Pcum; // cumulative probability
int32_t m_test_tag; // 自定义标签
Ptr<UniformRandomVariable> m_seqRng; // RNG
};
...
- 在ndn-consumer-zipf-mandelbrot-test.cpp中的
GetTypeId()
方法里添加属性设置接口:
ConsumerZipfMandelbrotTest::GetTypeId(void){
static TypeId tid =
TypeId("ns3::ndn::ConsumerZipfMandelbrotTest")
...
.AddAttribute("TestTag", "it is a test tag", IntegerValue(1),
MakeIntegerAccessor(&ConsumerZipfMandelbrotTest::m_test_tag),
MakeIntegerChecker<int32_t>()); // 默认为1
return tid;
}
- 在
SendPacket()
方法中对标签初始化:
void
ConsumerZipfMandelbrotTest::SendPacket()
{
...
shared_ptr<Interest> interest = make_shared<Interest>();
interest->setNonce(m_rand->GetValue(0, std::numeric_limits<uint32_t>::max()));
interest->setName(*nameWithSequence);
// 初始TestTag,注意此步要求已经为兴趣包添加了TestTag标签
interest->setTestTag(m_test_tag);
...
}
在示例文件中使用自定义的用户类
每次运行仿真实验时,我们仅需要在示例文件中修改TestTag的值即可,从而避免编译整个ConsumerZipfMandelbrotTest类。
...
ndn::AppHelper consumerHelper("ns3::ndn::ConsumerZipfMandelbrotTest");
consumerHelper.SetAttribute("TestTag", IntegerValue(1));
...