ns-3_ Day 2
ns-3的代码设计
ns-3的核心是时间调度和网络模拟的支持,在工程技术上有很多重要的设计值得了解。
核心模块
ns-3的内核指的就是 src/core/下的代码,而基于core定义的network模块给出了最基本的单元定义如packet。围绕这两个模块,可以将ns-3的结构描述为:
随机数
随机数生成器RNG的功能:
- 提供一个随机数序列(循环序列);
- 可以被分隔成几个相互独立的数据流。
我们总是希望一个RNG能提供一个非常长的循环序列,并有效地分配每一个数据流。
ns-3使用 MRG32k3a 生成器,保证足够的循环长度以及数据流数量。
一个生成随机数的例子:
#include "ns3/simulator.h" #include "ns3/nstime.h" #include "ns3/command-line.h" #include "ns3/rng-seed-manager.h" #include "ns3/random-vatiable-stream.h" #include <iostream> using namespace ns3; int main(int argc, char *argv[]) { CommandLine cmd; cmd.Parse(argc, argv); double min = 0.0; double max = 10.0; // 改变种子后会得到不同的随机数 // 如果不改变种子同时运行多组实验,可以通过SetRun()来设置组数保证实验的独立性 RngSeedManager::SetSeed(1); Ptr<UniformRandomVariable> uv = CreateObject<UniformRandomVariable>(); uv->SetAttribute("Min", DoubleValue(min)); uv->SetAttribute("Max", DoubleValue(max)); std::out << uv->GetValue() << std::endl; return 0; }
回调机制
针对静态函数
//... static double CbOne(double a, double b) { std::out << "invoke cbOne a=" << ", b=" << b << std::endl; return a; } int main(int argc, char* argv[]) { // 声明了一个叫“one”的回调,指定了返回值和两个参数都是double // 绑定CbOne为回调函数 Callback<double, double, double> one = MakeCallback(&CbOne); // ... NS_ASSERT(!one.IsNull()); double retOne = one(10.1, 11.2); }
针对类成员函数
class MyCb{ public: int CbTwo(double a){ std::out << "invoke cbTwo a=" << a << std::endl; } return -5; } MyCb cb; Callback<int, double> two = MakeCallback(&MyCb::CbTwo, &cb); NS_ASSERT(!two.IsNull()); int retTwo = two(10.0);
构建Null回调的方法
MakeNullCallback<int, double>();
Attribute系统
从下面的例子及注释中可以看清Attribute的用法
#include "ns3/log.h" #include "ns3/command-line.h" #include "ns3/ptr.h" #include "ns3/config.h" #include "ns3/uinteger.h" #include "ns3/string.h" #include "ns3/pointer.h" #include "ns3/simulator.h" #include "ns3/node.h" #include "ns3/queue.h" #include "ns3/drop-tail-queue.h" #include "ns3/point-to-point-net-device.h" using namespace ns3; int main(int argc, char *argv[]) { LogComponentEnable("AttributeValueSample", LOG_LEVEL_INFO); // 1. 设置默认值 Config::SetDefault("ns3::DropTailQueue::MaxPackets", StringValue("80")); Config::SetDefault("ns3::DropTailQueue::MaxPackets", UintegerValue("80")); CommandLine cmd; cmd.Parse(argc, argv); // 这里体现了ns-3使用模板创建对象的方法 CreateObject // Ptr是ns-3提供的计数指针 Ptr<Node> n0 = CreateObject<Node>(); Ptr<PointToPointNetDevice> net0 = CreateObject<PointToPointNetDevice>(); n0->AddDevice(net0); // 为node分配一个packet队列,它从尾部开始丢弃packet Ptr<Queue> q = CreateObject<DropTailQueue>(); net0->SetQueue(q); // ptr是一个指针变量,然后把net0的TxQueue赋值给ptr PointerValue ptr; // 2. 通过指针获取属性值 net0->GetAttribute("TxQueue", ptr); Ptr<Queue> txQueue = ptr.Get<Queue>(); Ptr<DropTailQueue> dtq = txQueue->GetQueue<DropTailQueue>(); NS_ASSERT(dtq); UintegerValue limit; dtq->GetAttribute("MaxPackets", limit); NS_LOG_INFO("1. dtq limit: " << limit.Get() << " packets"); txQueue->GetAttribute("MaxPackets", limit); NS_LOG_INFO("2. txQueue limit: " << limit.Get() << " packets"); txQueue->SetAttribute("MaxPackets", UintegerValue(60)); txQueue->GetAttribute("MaxPackets", limit); NS_LOG_INFO("3. txQueue limit changed: " << limit.Get() << " packets"); Config::Set("/NodeList/0/DeviceList/0/TxQueue/MaxPackets", UintegerValue(25)); txQueue->GetAttribute("MaxPackets", limit); NS_LOG_INFO("4. txQueue limit changed through namespace: " << limit.Get() << " packets"); Config::Set("/NodeList/*/DeviceList/*/TxQueue/MaxPackets", UintegerValue(15)); txQueue->GetAttribute("MaxPackets", limit); NS_LOG_INFO("5. txQueue limit changed through wildcarded namespace: " << limit.Get() << " packets"); Simulator::Destory(); }
除了上述两个方法,还有添加属性条目的方法:AddAttribute()
对于类TcpSocket的一个成员变量 uint32_tm_cWnd,假设使用TCP模块时想要试用元数据获取或设置该变量的值,如果ns-3还没有提供这个变量,那么用户可以在元数据系统中添加如下声明:
ptr->AddAttribute("Congestion window", "Tcp congestion window (bytes)", Uintergevalue(1), MakeUintegerAccessor(&TcpSocket::m_cWnd), MakeUintegerChecker<uint16_t>())
Tracing系统
拆解一个Tracing系统,其组成可以理解为:TracingSource、TracingSink以及关联它们的方法。
通过一个例子了解:
#include "ns3/object.h" #include "ns3/uinteger.h" #include "ns3/traced-value.h" #include "ns3/trace-source-accessor.h" #include <iostream> using namespace ns3; // Tracing系统和Attribute系统密切相关, // 所以每一个要trace的数据都必须属于一个特定的类。 class MyObject : public Object { public: static TypeId GetTypeId(void) { static TypeId tid = TypeId("MyObject") .SetParent(Object::GetTypeId()) .AddConstructor<MyObject>() .AddTraceSourceAccessor(&MyObject::m_myInt); // 此处的m_myInt被确定为一个TracingSource return tid; } MyObject(){} TraceValue<uint32_t> m_myInt; } // 函数IntTrace就是TraceSink void IntTrace(int oldValue, int newValue) { std::out << "Traced " << oldValue << " to " << newValue << std::endl; } int main(int argc, char *argv[]) { Ptr<MyObject> myObject = CreateObject<MyObject>(); // 这个函数将Source和Sink关联起来 // 当myObject.m_myInt改变时,IntTrace才会被调用 myObject->TraceConnectWithoutContext("MyInteger", MakeCallback(&IntTrace)); myObject->m_myInt = 1234; }
但是TraceConnectWithoutContext这样的函数很少被使用。
通常我们使用一个被叫作“Config Path”的子系统。
如何关联TraceSource和TraceSink
下面演示了如何使用Config关联TraceSource和TraceSink
using namespace std; // 这个函数就是TraceSink void CourseChange(string context, Ptr<const MobilityModel> model) { Vector posision = model->GetPosition(); NS_LOG_UNCOND(context << " x = " << position.x << " y = " << position.y); } // ... ostringstream oss; // 类MobilityModel的属性CourseChange是TraceSource oss << "/NodeList/" << wifiStaNodes.Get(nWifi-1)->GetId() << "/$ns3::MobilityModel/CourseChange"; Config::Connect(oss.str(), MakeCallback(&CourseChange));
oss.str()其实就是“ConfigPath”,起到了类似绑定的作用,每当MobilityModel的位置/速度的值变了就会进行trace。
如何确定TraceSource
所有可用的TraceSource在文档: ns-3: All TraceSources
如何确定TraceSink
其实就是定义一个函数并将其设置为回调。
该函数的确定步骤如下:
- 返回值为 void
- 参数列表的参数类型是 TraceSource 的变量类型
- 要是用 Config::Connect,那么参数列表的第一个参数还得加一个字符串参数
posted on 2022-12-19 10:45 LeewayTang 阅读(175) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本