5,这一个问题应该洞悉ns2的核心问题了,所以即使我现还有很多问题没有搞懂,我依然把握对核心代码的一些理解写出来。
在ns2中,应用代理使用来产生逻辑数据流或者说是数据流的发动者,而实际的数据流(包)却是在传输成代理(TCP/UDP等)那里产生的,我就的这样未免有点混乱——两个层面的代理需要相互的配合才能完成某一协议的模拟,不过既然是模拟这一点可能也就不重要了。要想理解数据的流动就不得不讲一下包的格式,以及模拟器对包的初始化过程。
首先:模拟器Simulator的init过程有这么一步:
$self create_packetformat
该方法在ns-packet.tcl中定义:
Simulator instproc create_packetformat { } {
PacketHeaderManager instvar tab_
set pm [new PacketHeaderManager]
foreach cl [PacketHeader info subclass] {
if [info exists tab_($cl)] {
set off [$pm allochdr $cl]
$cl offset $off
}
} //这个for循环就是在为每一个包头计算它在包头栈中的偏移量,具体方法也在此文件中(不过还没 //仔细研究,呵呵)
$self set packetManager_ $pm
}
PacketHeaderManager set tab_(Common) 1 //还要主义这么一句,就是说如果用户没有添加任何的 //包头,系统也会自动初始化一个Common包头。添加包头的tcl方法也在此文件中。
现在,看到这里肯定有有一个疑惑了,计算偏移量干什么呢,当然是要寻址了(这点和编译过程有点类似),用户为了能够对包头进行操作,只要用到这个偏移量就可以,多方便。下面举一个例子,在AODV路由算法的实现中,包括了一个包头的定义hdr_aodv,这个头结构中有一个access方法,通过该方法(因为在初始化Simulator的时候就已经算好offset的值了)在任何时候访问到该包头中的内容
宏定义:
#define HDR_AODV(p) ((struct hdr_aodv*)hdr_aodv::access(p))
例如:
struct hdr_aodv *ah = HDR_AODV(p);ah->ah_type
当然根据“惯例”为了Otcl于c++的互通要有一个类似于TclClass的类从当linkage
int hdr_aodv::offset_;
static class AODVHeaderClass : public PacketHeaderClass {
public:
AODVHeaderClass() : PacketHeaderClass("PacketHeader/AODV",
sizeof(hdr_all_aodv)) {
bind_offset(&hdr_aodv::offset_);
}
} class_rtProtoAODV_hdr;
就是这个类实现了同TclClass一样的机制。Packet就是一个包,大部分情况下只包括一个包头,我们暂且就称Packet为数据也不为过了。
ns2中数据是大体是这么传输的(引自ns by example):
FTP发出指令,TCP实际分配数据,而通过分类器传给下一个目标节点。我是从创建节点开始分析的,但还没有分析通整个过程。
Ns2创建一个node的过程是这样的:
Simulator instproc node args {
。。。。。
set node [eval new [Simulator set node_factory_] $args]
#default node_factory_ is Node
。。。。。
}
Node instproc init args {
。。。。。
$self mk-default-classifier
。。。。。
}
Node instproc mk-default-classifier {} {
Node instvar module_list_
# At minimum we should enable base module
foreach modname [Node set module_list_] {
$self register-module [new RtModule/$modname]
}
}
Node instproc register-module { mod } {
$self instvar reg_module_
$mod register $self
set reg_module_([$mod module-name]) $mod
}
RtModule/Base instproc register { node } {
$self next $node
$self instvar classifier_
set classifier_ [new Classifier/Hash/Dest 32]
$classifier_ set mask_ [AddrParams NodeMask 1]
$classifier_ set shift_ [AddrParams NodeShift 1]
# XXX Base should ALWAYS be the first module to be installed.
$node install-entry $self $classifier_
}
看到这里,发现一个单播节点在初始化的时候只是加载了一个地址分类器,可是看一很久还是没有发现everything上面所说的端口分类器,这一点还有待研究(???)。
创建节点之后就是在节点之间创建链路(无线网络除外,但是有一个channel对象为其提供类似服务),然后attach代理(应该注意的是这个过程是在解释器端也就是用tcl代码实现的,还包括路由选择等过程我还没有弄懂(???)),最后就可以发送数据了。
如果大家去看/ns2.33/common里的代码,可以发现用于构建网络拓扑结果的器件(如connector、agent等等),凡是设计到数据发送的都有这么一个方法recv,其实这个也是必然的,因为大部分的类都是集成connector和classifier,并且是在重写这个方法。Recv,顾名思义,就是用来接受数据的。而send方法有最后的一步就是target->recv(),所以任何一个数据包的流向都可以通过追踪recv来观察。这里只是粗略的说了一下,明白原理,毕竟我是个初学者,以后还要慢慢把细节补上。
6,软件调试是软件开发中的一项重要过程,同时反映调试信息的代码的编程量也是巨大的,而在ns2中网络模拟数据的采集其实就是输出软件调试信息,只是改把“探测器”放到那里的问题,“探测器”主要就是一些trace过程。只有探测到这些信息,才能利用分析工具进行分析。在/ns2.33/trace文件夹下,有
class Trace : public Connector,trace也是继承自connector说以可以看做是拓扑结构的一部分
:from ns by example
根据这个图我猜想,在建立link的时候trace就已经被安装在link中了,但是这个建立链路的过程以及ns2的整个建立拓扑结构的过程我还没有搞清楚,比如每个connnector的target是怎么设置的,这个有待下一步研究(???)。
根据
class CMUTrace : public Trace {
。。。。。
void format_mac(Packet *p, int offset);
void format_smac(Packet *p, int offset);
void format_ip(Packet *p, int offset);
。。。。。
} 可以猜想的到要想跟踪自己的协议数据,就要在这个类中添加自己的 trace 方法