脚本编写的有用经验

Helper

每一个模块都有其Helper,源代码位于./src/模块名/helper/下。例如:

setlinestyle参数 setattribute参数含义_回调函数

参数输入

attribute是网络模拟中用户可配置的参数,其实就是类的一个变量。例如在P2P网络的例子中,就有如下的设置传输速率的操作:

pointToPoint.SetDeviceAttribute("DataRate", StringValue("5Mbps"));

可以把attribute当成是参数输入。

配置attribute的方法有很多,可以分为如下3类:

setlinestyle参数 setattribute参数含义_ci_02

Helper

Helper的Set_____Attribute()方法实际上创建的是下一个对象的attribute默认值,因此也仅在对象创建时起作用。
SetAttribute()方法的参数有两个:属性名和属性值。
属性值的表示由类型和值组成。

ObjectBase::SetAttribute

ObjectBase是ns-3中绝大部分表示网络元素类的基类。
一次修改一个属性值,且针对的是已创建对象中的属性(例如要在模拟过程中改变一个网络设备的传输速率)。
基本用法同Helper。

Config::Set()

主要用于设置属性和trace变量。
参数是属性命名空间路径和值。例如:
Config::Set("/NodeList/0/DeviceList/0/$ns3::PointToPointNetDevice/DataRate", StringValue("2.5Mbps"));

trace设置

attribute是输入,那么trace就是输出。

对网络模拟中的一些重要网络行为进行记录,并以用户可配置的格式输出结果。

本质上也是成员变量(函数指针)。使用时,在脚本中预先定义一个回调函数,然后通过trace系统将其与某个C++对象内部的函数指针(trace变量)相关联。在模拟过程中,每当有特定的网络行为发生,ns-3就会调用相应的trace变量函数指针,触发回调函数。

通常我们把trace变量的配置放在网络拓扑和应用建立完成之后,因为此时各个网络层的主要C++对象已经建立完毕。但是也有例外,比如Socket对象在应用启动时才被创建,因此一般都用Schedule函数把配置trace变量的操作放在应用程序启动之后。

setlinestyle参数 setattribute参数含义_回调函数_03


下面对设置trace的方式进行举例说明:

void MacTxCallback(std::string context, Ptr<const Packet> packet){
	NS_LOG_UNCOND("+ " << Simulator::Now().GetSecond() << " " << packet->GetSize() << " " << context);
}
Config::Connect("/NodeList/*/DeviceList/*/$ns3::PointToPointNetDevice/MacTx", MakeCallback(&MacTxCallback));

上面的代码使用Config::Connect()将函数MacTxCallback赋予所有PointToPointNetDevice对象的trace变量MacTx。每当一个PointToPointNetDevice发送分组时,MacTxCallback函数就会被执行。
MacTxCallback的函数原型怎么确定?重点是参照trace变量的签名,除了第一个参数固定使用std::string context外,其余和trace变量相同即可。
Config::ConnectWithoutContext()函数不需要context参数,其余和Config::Connect()相同。
Helper设置trace实际上是封装了Config::Connect()和Config::ConnectWithoutContext()。

  1. NetDevice的trace配置:包括PointToPointHelper/CsmaHelper/WifiPhyHelper等。

setlinestyle参数 setattribute参数含义_回调函数_04

  1. InternetStackHelper

setlinestyle参数 setattribute参数含义_setlinestyle参数_05

命令行

CommandLine cmd;
cmd.AddValue("nWifi", "Number of wifi STA devices", nWifi);
cmd.Parse(argc, argv);

这样就可以在运行脚本的时候指定nWifi:
./waf --run "third --nWifi=18"

Schedule函数

在模拟过程中,如果需要在特定时间点执行某种操作,就会用到Simulator::Schedule()。
它的参数:

  • 相对延迟时间
  • 回调函数指针
  • 回调函数形参,最多支持6个形参

例如:

static void AdvancePosition(Ptr<Node> node);
Simulator::Schedule(Seconds(1.0), &AdvancePosition, ap.Get(0));

如果回调函数是类的成员函数,那么还需要添加一个对象指针参数:

void MyApp::ScheduleTx(void){
	m_sendEvent = Simulator::Schedule(tNext, &MyApp::SendPacket, this);
}

需要补充的是,ns-3的时间概念是虚拟的。除非使用实时模拟器RealtimeSimulatorImpl,否则在事件调度中,时间只决定调度顺序而不决定调度间隔。

Callback类

为了简化操作,ns-3把回调函数封装在Callback类里。
函数指针由类模板定义,可以定义返回值和最多9个形参。Callback对象的创建和赋值都由MakeCallback()函数来完成。
例如,下面是C++中回调的典型用法:

void MyFunction(int arg){}
class MyClass{
	void MyMethod(int arg);
};
void(*func)(int arg)=0;
void(MyClass::*memFunc)(int arg)=0;
int main(){
	func = MyFunction;	//将回调函数MyFunction地址赋给func
	func(1234);
	memFunc = &MyClass::MyMethod;
	MyClass myClass;
	(myClass.*memFunc)(1234);	//等同于myClass.MyMethod(1234)
	return 0;
}

用Callback类将改写成:

Callback<void, int> func;
func = MakeCallback(&MyFunction);
func(1234);	//等同于MyFunction(1234)
func = MakeCallback(&MyClass::MyMethod, &myClass);
func(1234);	//等同于myClass.MyMethod(1234)

除了MakeCallback,ns-3还提供了MakeBoundCallback来创建自带绑定参数的回调函数指针。
例如,下面的MyBoundFunction的信息都将被输出到func.txt中,这是因为stream作为绑定参数被自动添加。

void MyBoundFunction(Ptr<OutputStreamWrapper> stream, int arg){
	*stream->GetStream() << arg;
}
Callback<void, int> funcCallback;
AsciiTraceHelper asciiTraceHelper;
Ptr<OutputStreamWrapper> stream = asciiTraceHelper.CreateFileStream("func.txt");
funcCallback = MakeBoundCallback(&MyBoundFunction, stream);
funcCallback(1234);

这样,即使回调函数多了一个形参,Callback对象的格式仍未改变。

Log系统

trace系统不足以完全了解网络模拟行为和调试程序,因为:

  1. trace变量无法记录所有的网络事件
  2. trace变量存储在静态存储区,过多的trace变量不利于大规模模拟

Log系统是轻量级的,用于输出辅助信息。例如:

NS_LOG_INFO("At time " << Simulator::Now().GetSeconds() << "s client sent " 
			<< m_size << " bytes to " << Ipv4Address::ConvertFrom(m_peerAddress)
			<< " port " << m_peerPort);

NS_LOG_INFO内部调用的就是std::clog,但是要依附于LOG等级。

LOG可以设置等级或者前缀,用来控制哪些内容被输出。

等级:

setlinestyle参数 setattribute参数含义_setlinestyle参数_06


前缀:

setlinestyle参数 setattribute参数含义_setlinestyle参数_07


例如下面的代码,表示打印INFO以上的信息,并且输出该信息所属的节点ID和产生时间。

LogComponentEnable("UdpEchoClientApplication", 
					LogLevel(LOG_LEVEL_INFO|LOG_PREFIX_NODE|LOG_PREFIX_TIME));