ns-3中用到的常用技术主要分为:
- 参数输入:属性变量
- 数据输出:trace变量
- Schedule函数
- 回调函数:Callback类
本文主要介绍以上四个技术。至于辅助信息:Log系统、命令行和助手类——Helper技术就略过了。
参数输入:属性变量
属性其实就是c++类中的变量,它是内部私有成员变量,不过属性系统通过get、set方法使得它变成外部可配置的了。
1.配置属性
书中作者根据单次可配置属性的数量和新属性值的作用时间范围把它们划分为3类
(1)助手类、命令行和Config::SetDefault()
//助手类
PointToPointHelper pointToPoint;
pointToPoint.SetDeviceAttribute("DataRate",StringValue("5Mbpd"));
NetDeviceContainer device_1;
device_1 = pointToPoint.Install(nodes);
第一类配置属性只能在对象创建前进行配置,命令行与Config::SetDefault()也一样。Config::SetDefault()函数与Config::Set()函数使用方法类似,所以直接看Config::Set()函数的实现就可以。
(2)ObjectBase::SetAttribute()函数
Ptr<NetDevice> dev0 = nodes.Get(0)->GetDevice(0);
dev0->SetAttribute("DataRate",StringValue("5Mbpd"));
这类属性配置是在对象创建之后,不过一次只能配置一个属性
(3)Config::Set()
Config::Set("NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/DataRate",StringValue("5Mbpd"));
set函数第一个参数是属性的路径,成为属性命名空间路径,用于唯一标识一个已创建对象中的属性,如改代码指向的是第0个节点对象的第0个网络设备对象的DataRate属性,第二个参数就是属性的值了
2.读取属性
Ptr<NetDevice> dev0 = nodes.Get(0)->GetDevice(0);
StringValue value;
dev0->GetAttribute("DataRate",value);
读取属性用get,赋值到value中
3.查找属性
两种思路:查看属性所在类的GetTypeId()函数,或者查看官方文档中”Detail Description”中的”Attribute”部分。
数据输出:trace变量
参考前面的《ns-3-Tracing系统详解》,这里简单说明一下,trace变量是一个函数指针。因为是个指针,所以必须要有对象创建出来才可以给指针赋值,所以配置trace变量一般都在对象创建之后,不过也有例外,如Socket对象。(以后再讲)
1.配置trace变量
Config::Connect()
void MacTxCallback(std::string context, Ptr<const Packet> packet)
{
//...具体代码实现
...
}
Config::Connect("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx",MakeCallback(&MacTxCallback));
与Config::Set()的用法类似,第一个参数是trace变量命名空间路径,这段代码将回调函数指针赋值给了所有节点的所有网络设备。
另外两个配置trace的函数是ObjectBase::TraceConnect()与ObjectBase::TraceConnectWithoutContext(),只不过只能一次配置一个对象的trace变量。
ObjectBase::TraceConnect()与ObjectBase::TraceConnectWithoutContext()直接从代码中获取对象,
而Config::Connect()与Config::ConnectWithoutContext()从命名空间中获取对象
2.查找trace变量
和查找属性变量十分类似
(1)GetTypeId()函数
trace变量是GetTypeId()中的AddTraceSource()函数创建的
(2)官方文档
前面的tracing系统详解中讲过了
Schedule函数
用来控制程序运行时间的,可以用来解决Socket对象创建之前无法给函数指针赋值的问题:将函数指针赋值操作放在Schedule函数中,设置好时节,正好Socket对象创建完成之后,再进行指针赋值操作,这样就可以解决问题了。
Schedule函数也是通过回调来实现的(这里指的是需要进行延迟的函数是通过回调方式调用的),最多支持6个回调函数形参
static void AdvancePosition(Ptr<Node> node);
//第一个参数是事件发生延迟时间,
//第二个参数是事件回调函数指针
//第三个参数是事件回调函数的参数,最多支持6个
Simulator::Schedule(Seconds(1.0), &AdvancePosition, ap.Get(0));
类内成员函数也可以
void MyApp::ScheduleTx(void){
//第三个参数是回调函数所属的C++对象指针
m_sendEvent = Simulator::Schedule(Seconds(1.0), &MyApp::SendPacket, this);
}
回调函数:Callback类
c++中回调函数经典用法:
//回调函数
void MyFunction(int arg){}
//成员回调函数
class MyClass{
void MyMethod(int arg);
}
//定义并初始化函数指针
void (*func)(int arg) = 0;
void (MyClass::*memFunc)(int arg) = 0;
int main(){
//将回调函数MyFunction()地址赋值给func函数指针
func = MyFunction;
//调用回调函数:等同于MyFunction(1234)
func(1234);
//将成员回调函数MyMethod()地址赋值给memFunc函数指针
memFunc = &MyClass::MyMethod;
MyClass myClass;
//调用回调函数:等同于myClass.MyMethon(1234)
(myClass.*memFunc)(1234);
return 0;
}
而再ns3中,封装在了Callback类模板中。而Callback对象创建与赋值由MakeCallback()函数完成。
//定义回调函数签名格式:返回值void,形参int
Callback<void, int> funcCb;
//将回调函数MyFunction()地址赋值给funcCb对象内部的函数指针(Callback里会定义一个接收函数的函数指针)
funcCb=MakeCallback(&MyFunction);
//调用
funcCb(1234);
//成员回调
//将成员回调函数MyMethod()地址赋值给funcCb对象内部的函数指针
funcCb=MakeCallback(&MyClass::MyMethod,&myClass);
//调用
funcCb(1234);
ns3还提供了一种创建自带绑定参数的回调函数指针的方法MakeBoundCallback()
void MyBoundFunction(Ptr<OutPutStreamWrapper> stream, int arg)//绑定参数总是位于形参列表前几项
{}
//模板中依然是两项,不需要stream
Callback<void, int> funcCallback;
//定义一个stream
AsciiTraceHelper asciiTraceHelper;
Ptr<OutPutStreamWrapper> stream = asciiTraceHelper.CreateFileStream("func.txt");
//添加一个绑定形参
funcCallback = MakeBoundCallback(&MyBoundFunction, stream);
//调用时形参会自动添加,我们只需要写一个参数即可
funcCallback(1234);
最多3个绑定形参,总的形参数最多9个。
本文主要来自于《开源网络模拟器ns-3架构与实践》,周迪之著。代码图片均来自此书。