对象模型
ns-3对象模型通过3个基类Object、ObjectBase、SimpleRefCount定义了ns-3中绝大部分C++类的行为和关系。
- SimpleRefCount解决针对单个类的动态内存管理问题。这在前面的Ptr已经讲了。
- ObjectBase解决配置属性和trace变量的问题。它定义了对这些变量进行配置的方法和存储数据结构。
- Object解决多个类的动态关联问题,解决的方法是对象聚合(Object Aggregation)。可以让一个Object对象在运行时关联其他对象。
Object:理解对象聚合
对象聚合就是对象关联,这些对象都是Object的派生类。
看一段Object的代码:
class Object : public SimpleRefCount<Object, ObjectBase, ObjectDeleter>
{
public:
//添加聚合对象
void AggregateObject(Ptr<Object> other);
//依据类名查找对应对象
template <typename T>
inline Ptr<T> GetObject(void) const;
private:
//聚合数组
struct Aggregates
{
uint32_t n; //聚合数组元素数量
Object *buffer[1]; //聚合数组起始指针
};
struct Aggregates *m_aggregates; //聚合数组
}
一个Object的派生类持有一个聚合数组m_aggregates,包含其所有的关联对象。这些关联对象彼此地位平等,持有的聚合数组也是完全一样的。
创建Object
- 创建ObjectFactory对象
ObjectFactory pppF;
- 设置对象属性
pppF.SetTypeId(); pppF.Set();
pppF.Create<>();
GetObject
查找关联对象,用法如下:
Ptr<Ipv4> ipv4 = node->GetObject<Ipv4>();
Ptr<Ipv4L3Protocol> ipv4 = node->GetObject<Ipv4L3Protocol>();
上面两行是等价的,只是查找到的指针类型不同(Ipv4是Ipv4L3Protocol的基类)。因此GetObject的另一个用法是指针上下行转换:
Ptr<Ipv4L3Protocol> ipv4L3Prot = ipv4->GetObject<Ipv4L3Protocol>();
查找失败则返回空指针。
元信息
GetObject的技术原理是元信息(meta information)。元信息是关于ObjectBase及其派生类的辅助信息,每一个派生类具有唯一的一组元信息。
所有元信息被存储在一个vector中,类名作为查找关键字,实体类是IidManager。
ObjectBase的派生类会保存其元信息的索引值,保存在TypeId之中。可以把元信息的结构理解成:
查找的实现思路:
- 获取模板中类的TypeId tid;
- 把tid和聚合数组中的每个元素的TypeId进行比较;
- 把tid和聚合数组中的每个元素的基类TypeId进行比较;
举例:GetObject[ipv6L3Protocol, ipv4L3Protocol]
的搜索路径
ObjectBase:实现配置功能
前面已经看到,属性和trace信息保存在元信息之中。实际上属性系统和trace系统就是元信息以及对应的函数。
属性
属性值类型不可以互转,但都可以转换成StringValue。
- ConfigStore可以用于保存脚本中用过的属性集为一个文件,并在下一次模拟中加载。
- 全局属性不属于任何一个类,都是一些影响模拟器行为或者性能的变量。ns-3支持的全局属性有限。
举例:PointToPointNetDevice的传输速率属性
class PointToPointNetDevice : public NetDevice
{
private:
DataRate m_bps;
};
TypeId PointToPointDevice::GetTypeId(void)
{
static TypeId tid = TypeId("ns3::PointToPointNetDevice")
.AddAttribute(
"DataRate", //属性名
DataRateValue(DataRate("32768b/s")), //默认值
MakeDataRateAccessor(&PointToPointNetDevice::m_bps));//创建了一个属性访问函数并将函数指针存放在属性辅助信息中
return tid;
}
trace
trace变量本质上是一个函数指针的成员变量,trace系统的作用就是把函数指针变成一个可以配置的变量。
举例:一个PointToPointDevice的分组接收trace
class PointToPointNetDevice : public NetDevice
{
private:
TracedCallback<Ptr<const Packet>> m_macRxTrace;
};
TypeId PointToPointDevice::GetTypeId(void)
{
static TypeId tid = TypeId("ns3::PointToPointNetDevice")
.AddTraceSource(
"MacRx", //trace名
MakeTraceSourceAccessor(&PointToPointNetDevice::m_macRxTrace), //创建了一个trace访问函数并将其函数指针存放在trace辅助信息中
"ns3::Packet::TracedCallback"); //指定回调函数的签名是Packet类的TracedCallback函数指针
return tid;
}
void PointToPointNetDevice::Receive(Ptr<Packet> packet)
{
m_macRxTrace(originalPacket); //使用回调的方法
}
当m_macRxTrace作为一个TracedCallback被声明后,它就成为了一个可以在脚本中配置的trace变量。
和属性辅助信息一样,trace变量的辅助信息也在初始化TypeId的时候指定。
ns-3的trace变量的签名格式主要有两种:
- TracedCallback类型 主要用于和分组相关的行为事件(如分组收发、丢失)
- 返回值为void(因为这些trace变量只用作提供数据,不需要返回),形参最多8个
- 签名格式命名习惯为
ns3::类名或命名空间::typedef名
,举例:- Ipv4L3Protocol类的分组发送trace变量Tx的签名格式:
ns3::Ipv4L3Protocol::TxRxTracedCallback
; - 使用最广泛的签名格式是
ns3::Packet::TracedCallback
- Ipv4L3Protocol类的分组发送trace变量Tx的签名格式:
- trace变量对应的函数指针都是TracedCallback类的对象
- TracedValue类型 主要用于一个数值变量的大小变化事件(如TCP拥塞窗口大小变化、一个队列中分组数目变化)
- 其内部就是只有两个形参的TracedValueCallback对象
- 只需要记录旧数值和新数值两个变量即可