在NS2中添加一个新的协议
简介:
在NS中添加一个新的网络元素通常都需要是Otcl 链接到C++类。在NS2中的元素的实现都是用C++编写的主要是出于执行效率的考虑。在这篇文章中会介绍在NS2中添加一个新的元素来理解C++/OTcl是如何链接的。在这篇文章中我们用以个新的简单的协议“MyAgent”,这个协议没有具体的方法。
1.扩展C++类到OTcl
假设你已经新建了一个C++类,在这篇文章中叫“MyAgent”,它是从“Agent”类继承而来的,你当然想通过OTcl中创建一个“MyAgent”的实例对象,这就需要定义一个链接类,在这儿我们叫做“MyAgentClass”,它是从”TclClass“中继承而来的。这个链接类的对象创建一个OTcl名为“Agent/MyAgentOtcl”对象,同时注册了OTcl对象和C++对象的对应关系。见下面的代码:
class MyAgent : public Agent {
public:
MyAgent();
protected:
int command(int argc, const char*const* argv);
private:
int my_var1;
double my_var2;
void MyPrivFunc(void);
};
static class MyAgentClass : public TclClass {
public:
MyAgentClass() : TclClass("Agent/MyAgentOtcl") {}
TclObject* create(int, const char*const*) {
return(new MyAgent());
}
} class_my_agent;
当NS 最开始启动时,它就会执行这个静态变量“class_my_agent”的构造函数,从而使“MyAgentClass”实例化了一个对象。在这个过程中,在OTcl的空间中同时也创建了一个名字为“Agent/MyAgentOtcl”的类。不论何时当一个用户在OTcl中用命令“new Agent/MyAgentOtcl”就会创建一个这个类的对象,它同时也会引发“MyAgentClass::create”创建一个“MyAgent”的实例并且返回一个地址(这个地址是保存在Tcl类中的哈希表中的)。值得注意的是能够通过OTcl创建一个C++对象并不意为这你可以在OTcl中访问它的属性和方法。这个问题我们会在下面的内容中解决。
2.扩展C++类中的属性到OTcl
在上面所建的“MyAgent”类中,有两个属性“my_var1”和“my_var2”,当然我们想在OTcl中通过仿真脚本能够很轻松的配置他们。为了做到这个,你就应该为每个你想要扩展的属性用binding函数。这个binding函数会在OTcl中对应的类(这儿是“Agent/MyAgentOtcl”)中创建一个名字为binding函数第一个参数的值的成员变量,同时会把C++中的变量的地址(binding函数的第二个参数)和这个变量绑定起来。见下面的代码:
MyAgent::MyAgent() : Agent(PT_UDP) {
bind("my_var1_otcl", &my_var1);
bind("my_var2_otcl", &my_var2);
}
注意binding函数是在“MyAgent”类的构造函数中调用的,NS支持四种不同的类型的绑定(因为C++是一个强类型的语言而Tcl中只有字符串一种类型,这就需要一个转换)。
a. bind(): 绑定整型和实数类型的变量
b. bind_time(): 绑定时间变量
c. bind_bw(): 绑定带宽变量
d. bind_bool(): 绑定boolean变量
通过上述方法,用户就可以通过OTcl脚本来改变用C++编写的网络元素的属性的值。值得注意的是,当你想要扩展一个C++变量是,希望你能够在“~ns/tcl/lib/ns-default.tcl”中设定这个变量的初始值,否则你会在创建一个对象的时候收到警告信息。
3.扩展C++类的方法到Otcl
除了扩展一些C++属性之外,你也许会想通过OTcl来实现C++类中的方法。这时你需要在你的C++类“MyAgent”增加一个名为“command”的函数,这个函数就像是OTcl命令的一个解释器。实际上,一个在“command”中定义的OTcl命令就像是OTcl对象种的成员函数。见如下代码:
int MyAgent::command(int argc, const char*const* argv) {
if(argc == 2) {
if(strcmp(argv[1], "call-my-priv-func") == 0) {
MyPrivFunc();
return(TCL_OK);
}
}
return(Agent::command(argc, argv));
}
当在OTcl中一个名为“MyAgent”类的影像对象被创建时,用户试图通过一些命令来调用改影像对象的方法($myagent call-my-priv-func),OTcl就会会寻找在OTcl对象种的call-my-priv-func实例过程。如果这个过程没有被找到,这个时候就会触发“MyAgent::command”函数的执行,这个函数的参数是从OTcl对象以argc/argv的形式获得的。如果找到了这个命令,那就回去执行这个命令并从中返回一个结果。
如果没有找到相应的命令,那么就会递归的去调用”MyAgent“父类的“command”函数。如果在它的父类中找到相应的命令则执行,否则继续向上找,如果没有找到那么就返回错误消息。
4.在C++中执行OTcl命令
当你想在C++中执行OTcl命令时,下面的代码给出了如果在“MyPrivFunc”中执行OTcl命令:
void MyAgent::MyPrivFunc(void) {
Tcl& tcl = Tcl::instance();
tcl.eval("puts \"Message From MyPrivFunc\"");
tcl.evalf("puts \" my_var1 = %d\"", my_var1);
tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}
为了能够在C++中执行OTcl命令,你需要通过“Tcl::instance()"得到一个Tcl实例的引用(去阅读Tcl类的源代码你会发现这个实例是个静态变量)。它就相当于提供了OTcl命令的解释器的接口。
5.编译,运行和测试”MyAgent“
a.把你写好的代码添加到ns安装目录下的ns目录下,最好是建立一个自己的文件夹
b.修改ns下的Makefile,把你要编译的文件加进去
c.重新编译你的NS,使用”make“命令
d.编写仿真Tcl文件
e.执行你的仿真Tcl文件
附C++代码和Tcl代码:
#include <stdio.h>
#include <string.h>
#include "agent.h"
class MyAgent : public Agent {
public:
MyAgent();
protected:
int command(int argc, const char*const* argv);
private:
int my_var1;
double my_var2;
void MyPrivFunc(void);
};
static class MyAgentClass : public TclClass {
public:
MyAgentClass() : TclClass("Agent/MyAgentOtcl") {}
TclObject* create(int, const char*const*) {
return(new MyAgent());
}
} class_my_agent;
MyAgent::MyAgent() : Agent(PT_UDP) {
bind("my_var1_otcl", &my_var1);
bind("my_var2_otcl", &my_var2);
}
int MyAgent::command(int argc, const char*const* argv) {
if(argc == 2) {
if(strcmp(argv[1], "call-my-priv-func") == 0) {
MyPrivFunc();
return(TCL_OK);
}
}
return(Agent::command(argc, argv));
}
void MyAgent::MyPrivFunc(void) {
Tcl& tcl = Tcl::instance();
tcl.eval("puts \"Message From MyPrivFunc\"");
tcl.evalf("puts \" my_var1 = %d\"", my_var1);
tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}
-----------------------end C++ code ---------------------------------
set myagent [new Agent/MyAgentOtcl]
# Set configurable parameters of MyAgent
$myagent set my_var1_otcl 2
$myagent set my_var2_otcl 3.14
# Give a command to MyAgent
$myagent call-my-priv-func
------------------------end Tcl code------------------------------------
在NS中添加一个新的网络元素通常都需要是Otcl 链接到C++类。在NS2中的元素的实现都是用C++编写的主要是出于执行效率的考虑。在这篇文章中会介绍在NS2中添加一个新的元素来理解C++/OTcl是如何链接的。在这篇文章中我们用以个新的简单的协议“MyAgent”,这个协议没有具体的方法。
1.扩展C++类到OTcl
假设你已经新建了一个C++类,在这篇文章中叫“MyAgent”,它是从“Agent”类继承而来的,你当然想通过OTcl中创建一个“MyAgent”的实例对象,这就需要定义一个链接类,在这儿我们叫做“MyAgentClass”,它是从”TclClass“中继承而来的。这个链接类的对象创建一个OTcl名为“Agent/MyAgentOtcl”对象,同时注册了OTcl对象和C++对象的对应关系。见下面的代码:
class MyAgent : public Agent {
public:
MyAgent();
protected:
int command(int argc, const char*const* argv);
private:
int my_var1;
double my_var2;
void MyPrivFunc(void);
};
static class MyAgentClass : public TclClass {
public:
MyAgentClass() : TclClass("Agent/MyAgentOtcl") {}
TclObject* create(int, const char*const*) {
return(new MyAgent());
}
} class_my_agent;
当NS 最开始启动时,它就会执行这个静态变量“class_my_agent”的构造函数,从而使“MyAgentClass”实例化了一个对象。在这个过程中,在OTcl的空间中同时也创建了一个名字为“Agent/MyAgentOtcl”的类。不论何时当一个用户在OTcl中用命令“new Agent/MyAgentOtcl”就会创建一个这个类的对象,它同时也会引发“MyAgentClass::create”创建一个“MyAgent”的实例并且返回一个地址(这个地址是保存在Tcl类中的哈希表中的)。值得注意的是能够通过OTcl创建一个C++对象并不意为这你可以在OTcl中访问它的属性和方法。这个问题我们会在下面的内容中解决。
2.扩展C++类中的属性到OTcl
在上面所建的“MyAgent”类中,有两个属性“my_var1”和“my_var2”,当然我们想在OTcl中通过仿真脚本能够很轻松的配置他们。为了做到这个,你就应该为每个你想要扩展的属性用binding函数。这个binding函数会在OTcl中对应的类(这儿是“Agent/MyAgentOtcl”)中创建一个名字为binding函数第一个参数的值的成员变量,同时会把C++中的变量的地址(binding函数的第二个参数)和这个变量绑定起来。见下面的代码:
MyAgent::MyAgent() : Agent(PT_UDP) {
bind("my_var1_otcl", &my_var1);
bind("my_var2_otcl", &my_var2);
}
注意binding函数是在“MyAgent”类的构造函数中调用的,NS支持四种不同的类型的绑定(因为C++是一个强类型的语言而Tcl中只有字符串一种类型,这就需要一个转换)。
a. bind(): 绑定整型和实数类型的变量
b. bind_time(): 绑定时间变量
c. bind_bw(): 绑定带宽变量
d. bind_bool(): 绑定boolean变量
通过上述方法,用户就可以通过OTcl脚本来改变用C++编写的网络元素的属性的值。值得注意的是,当你想要扩展一个C++变量是,希望你能够在“~ns/tcl/lib/ns-default.tcl”中设定这个变量的初始值,否则你会在创建一个对象的时候收到警告信息。
3.扩展C++类的方法到Otcl
除了扩展一些C++属性之外,你也许会想通过OTcl来实现C++类中的方法。这时你需要在你的C++类“MyAgent”增加一个名为“command”的函数,这个函数就像是OTcl命令的一个解释器。实际上,一个在“command”中定义的OTcl命令就像是OTcl对象种的成员函数。见如下代码:
int MyAgent::command(int argc, const char*const* argv) {
if(argc == 2) {
if(strcmp(argv[1], "call-my-priv-func") == 0) {
MyPrivFunc();
return(TCL_OK);
}
}
return(Agent::command(argc, argv));
}
当在OTcl中一个名为“MyAgent”类的影像对象被创建时,用户试图通过一些命令来调用改影像对象的方法($myagent call-my-priv-func),OTcl就会会寻找在OTcl对象种的call-my-priv-func实例过程。如果这个过程没有被找到,这个时候就会触发“MyAgent::command”函数的执行,这个函数的参数是从OTcl对象以argc/argv的形式获得的。如果找到了这个命令,那就回去执行这个命令并从中返回一个结果。
如果没有找到相应的命令,那么就会递归的去调用”MyAgent“父类的“command”函数。如果在它的父类中找到相应的命令则执行,否则继续向上找,如果没有找到那么就返回错误消息。
4.在C++中执行OTcl命令
当你想在C++中执行OTcl命令时,下面的代码给出了如果在“MyPrivFunc”中执行OTcl命令:
void MyAgent::MyPrivFunc(void) {
Tcl& tcl = Tcl::instance();
tcl.eval("puts \"Message From MyPrivFunc\"");
tcl.evalf("puts \" my_var1 = %d\"", my_var1);
tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}
为了能够在C++中执行OTcl命令,你需要通过“Tcl::instance()"得到一个Tcl实例的引用(去阅读Tcl类的源代码你会发现这个实例是个静态变量)。它就相当于提供了OTcl命令的解释器的接口。
5.编译,运行和测试”MyAgent“
a.把你写好的代码添加到ns安装目录下的ns目录下,最好是建立一个自己的文件夹
b.修改ns下的Makefile,把你要编译的文件加进去
c.重新编译你的NS,使用”make“命令
d.编写仿真Tcl文件
e.执行你的仿真Tcl文件
附C++代码和Tcl代码:
#include <stdio.h>
#include <string.h>
#include "agent.h"
class MyAgent : public Agent {
public:
MyAgent();
protected:
int command(int argc, const char*const* argv);
private:
int my_var1;
double my_var2;
void MyPrivFunc(void);
};
static class MyAgentClass : public TclClass {
public:
MyAgentClass() : TclClass("Agent/MyAgentOtcl") {}
TclObject* create(int, const char*const*) {
return(new MyAgent());
}
} class_my_agent;
MyAgent::MyAgent() : Agent(PT_UDP) {
bind("my_var1_otcl", &my_var1);
bind("my_var2_otcl", &my_var2);
}
int MyAgent::command(int argc, const char*const* argv) {
if(argc == 2) {
if(strcmp(argv[1], "call-my-priv-func") == 0) {
MyPrivFunc();
return(TCL_OK);
}
}
return(Agent::command(argc, argv));
}
void MyAgent::MyPrivFunc(void) {
Tcl& tcl = Tcl::instance();
tcl.eval("puts \"Message From MyPrivFunc\"");
tcl.evalf("puts \" my_var1 = %d\"", my_var1);
tcl.evalf("puts \" my_var2 = %f\"", my_var2);
}
-----------------------end C++ code ---------------------------------
set myagent [new Agent/MyAgentOtcl]
# Set configurable parameters of MyAgent
$myagent set my_var1_otcl 2
$myagent set my_var2_otcl 3.14
# Give a command to MyAgent
$myagent call-my-priv-func
------------------------end Tcl code------------------------------------
缓慢行走的蜗牛