NS2的NODE类——node

来源:互联网 发布:学java好的学校 编辑:程序博客网 时间:2024/06/16 23:16

本文转自:http://hi.baidu.com/wirelesscat/blog/item/67c6db4633f71e016b63e59b.html

同时推荐一个很好的博客,这里有连载的 ns2 仿真问题,感谢大牛~~~

博客地址:http://hi.baidu.com/wirelesscat/blog/category/ns-2


Node是网络拓扑的重要组成部分,时NS2复合网络组件的一个大类。复合组件(如node和link)不是TclObject类的派生类,而是NS2中独立的类,一个ns2节点本质是一个分类器的集合。

节点结构图:
每个节点至少包含一下几个部分:一个地址或者id_,初始值是0,当节点建立时模拟的名字空间将自动加1;一个邻居链表neighbor_;一个代理链表agent_;一个节点类型识别器nodetype_;一个路由模块。
Tcl对象:1)地址分类器(address classifier),用来判断分组的目标地址,把传入的包分派到正确的链路。
~ns/classifier/classifier-addr.{h/cc};
            2)端口分类器(port classifier),用来判断分组的目标Agent,把传入的包分派到争取的代理。~ns/classifier/classifier-port.{ h/cc}。

节点建立:
set ns [new Simulator]                      set ns [new Simulator -multicast on]
$ns node
设置节点:(控制函数、地址和端口号管理、代理管理、添加邻居)
控制函数:$node entry:返回节点入口地址;$node reset:重新设置节点上所有代理。
地址和端口号管理:$node id:返回节点的节点号;$node agent(port):返回一个端口号为port的代理的句柄;
             alloc-port:返回下一个可获得的端口号;
             add-route/routes:单播时增加路由路径从而产生classifier_。$node add-route <destination id> <TclObject>
                                       $node add-routes <destination id> <TclObjects>添加多重路由路径到同一个目的地。
             delete-routes():包括参数id,一个TclObject的列表和一个模拟器的空代理的指针。
代理管理:attach():把制定的代理加入到一个agents_链表中,给该代理分配一个端口号并设置它的源地址。设置代理的指针为它的入口。例如节点的entry()添加一个节点上的多路服用的端口(dmux_)的指针到代理相应的dmux_分类器。相反的detach()从代理
agents_中移除代理,代理目标指针指向空代理的入口。
添加邻居:过程add-neighbor()用于增加一个邻居到这个链表中,neighbors()用于返回邻居链表。

配置节点:
  使用函数simulator::node-config{}配置即将创建的节点属性,这种节点配置方法主要针对移动节点和卫星。
  e.g: 

#设定模拟需要的一些属性,比如mobilenode的Channel、MAC、LL层的类型,天线类型,节点数目,场景的长宽尺寸等。
  set val(chan) Channel/WirelessChannel ;#信道类型
  set val(prop) Propagation/TwoRayGround ;#无线-传播模型
  set val(netif) Phy/WirelessPhy ;#网络接口类型
  set val(mac) Mac/802_11 ;#MAC类型
  set val(ifq) Queue/DropTail/PriQueue ;#接口队列类型
  set val(ll) LL ;#链路层类型
  set val(ant) Antenna/OmniAntenna ;#天线模型
  set val(ifqlen) 50 ;#ifq中的最大分组
  set val(nn) 2 ;#移动节点数
  set val(rp) AODV ;#路由协议
  set val(x) 500 ;#拓扑结构的X轴范围
  set val(y) 500 ;#拓扑结构的Y轴范围
  $set ns [new Simulator]
#建立节点(mobilenode)之前,先配置节点的一些参数。agentTrace表示应用层的trace,在trace文件中用#AGT表示;routerTrace表示路由的trace,在toace文件中用RTR表示;macTrace表示MAC层的trace,
#在trace文件中用MAC表示;movementTrace表示记录节点移动命令的trace,在trace文件中用M表示。
  $ns node-config -addressType def\ ;#设定节点地址类型:def(flat) & hierarchical
  -adhocRouting $val(rp) \ ;#设定移动节点所使用的路由协议
  -llType $val(ll) \ ;#设定移动节点的逻辑链路层
  -macType $val(mac) \ ;#设定移动节点的MAC层
  -ifqType $val(ifq) \ ;#设定移动节点的队列类型
  -ifqLen $val(ifqlen) \ ;#设定移动节点的队列长度
  -antType $val(ant) \ ;#设定移动节点的天线类型
  -propType $val(prop) \ ;#设定移动节点的无线信号传输模型
  -phyType $val(netif) \ ;#设定移动节点物理层类型
  -channelType $val(chan) \ ;#设定移动节点的无线信道类型
  -topoInstance $topo \ ;#设定移动节点的拓扑对象
  -agentTrace ON \ ;#是否打开应用层的trace
  -routerTrace ON \ ;#是否打开路由的trace
  -macTrace OFF \ ;#是否打开MAC层的trace
  -movementTrace OFF ;#是否打开节点位置和移动信息的trace

#建立两个节点(mobilenode),关闭节点的随机运动功能,即节点的运动完全由我们指定。
  for {set i 0} {$i > shift_) & mask_); }
   virtual void recv(Packet* p, Handler* h);
  //recv()函数的实现参考源文件~ns/classifier/classifier.cc
   virtual NsObject* find(Packet*);
  // find()函数的实现参考源文件~ns/classifier/classifier.cc
   virtual int classify(Packet *);
   virtual void clear(int slot); //从表中删除对象
   virtual void install(int slot, NsObject*); //从表中增加对象
  protected:
   virtual int command(int argc, const char*const* argv);
   void alloc(int); // alloc()函数动态的分配足够的空间来放置slot
   NsObject** slot_; /* table that maps slot number to a NsObject */
   int nslot_;
   int maxslot_;
   int offset_; // offset for Packet::access()
   int shift_;
   int mask_;
   NsObject *default_target_;
   int nsize_; //what size of nslot_ should be
  };
  ——————————————————————————————————
  e.g:~ns/classifier/classifier.cc
  void Classifier::recv(Packet* p, Handler*h)
  {
   NsObject* node = find(p);
   if (node == NULL) {
   Packet::free(p);
   return;
   }
   node->recv(p,h);
  }
  NsObject* Classifier::find(Packet* p)
  {
   NsObject* node = NULL;
   int cl = classify(p);
   if (cl = nslot_ || (node = slot_[cl]) == 0) {
   if (default_target_)
   return default_target_;
   Tcl::instance().evalf("%s no-slot %ld", name(), cl);
   if (cl == TWICE) {
   cl = classify(p);
   if (cl = nslot_ || (node = slot_[cl]) == 0)
   return (NULL);
   }
   }
   return (node);
  }
  当classifier收到一个分组时(即recv()函数被调用时),recv()函数首先调用find()函数来处理该分组,find()函数会调用classify()函数,不同类型的classifier对classify()函数定义不同,但各种classify函数都会对分组作检查然后返回一个slot索引值。如果索引值有效,并指向一个有效的TclObject,recv()函数就会把分组传递给该TclObject,即调用该TclObject的recv()函数;如果索引无效,就会清除该分组并返回。
  ——————————————————————————————————
  几种主要的Classifier类:
  1.address classifier:按分组目的地址进行匹配,用来支持单播分组转发,通过对分组的目的地址做位运算来产生一个slot number。该类对应的classify()函数定义参考~ns/classifier/classifier-addr.cc。
  ——————————————————————————————————
  int AddressClassifier::classify(Packet *p)
  {
   hdr_ip* iph = hdr_ip::access(p);
   return mshift(iph->daddr());
  };
  iph->daddr()函数返回分组IP头中的目的地址,mshift(int val)函数的定义在前面Classifier类的定义中可以找到,其中mask_和shift_的值可以通过Otcl来设置,缺省值分别为0xffffffff和0,即mshift(int val)直接返回val的值。
  ——————————————————————————————————
  2.port classifier:按分组目的端口进行匹配,将分组传递给相应的Agent对象。该类对应的classify()函数定义参考~ns/classifier/classifier-addr.cc。
 ——————————————————————————————————
  int PortClassifier::classify(Packet *p)
  {
   hdr_ip* iph = hdr_ip::access(p);
   return iph->dport();
  };
  iph->dport()函数返回分组IP头中的目的端口。
  ——————————————————————————————————
  3.replicator:与普通classifier不同,不使用classify()函数,作用是生成一个分组的多份拷贝,并把这些拷贝转发给slot表中的所有对象。在组播分组转发时,一个分组需要被转发给多个目标对象,生成分组拷贝的工作由replicator完成。Replicator类的定义及其recv()函数的定义参考~ns/mcast/replicator.cc。
  ——————————————————————————————————
  class Replicator : public Classifier
  {
  public:
   Replicator();
   void recv(Packet*, Handler* h = 0);
   virtual int classify(Packet*) {/*NOTREACHED*/ return -1;};
  protected:
   virtual int command(int argc, const char*const* argv);
   int ignore_;
   int direction_;
  };
  void Replicator::recv(Packet* p, Handler*)
  {
   hdr_ip* iph = hdr_ip::access(p);
   hdr_cmn* ch = hdr_cmn::access(p);
   if (maxslot_ saddr(), iph->daddr(), ch->iface());
   Packet::free(p);
   return;
   }
  ……
   for (int i = 0; i recv(p->copy());
   }
   /* we know that maxslot is non-null */
   slot_[maxslot_]->recv(p);
  }
  可以看到Replicator类的recv()函数并不调用classify()函数,它只是为slot表中的每一个对象复制一份该分组的拷贝,并把拷贝发送给该对象,表中的最后一个对象收到的是原始的分组,而原始分组和拷贝实际上是完全一样的。


  ——————————————————————————————————
  Classifier类提供了一些Tcl实例过程,用户可以在Tcl中对Classifier对象进行控制。主要实例过程:
  1. alloc-port :寻找一个空闲的slot。
  2. clear :将号码为index的slot清空。
  3. installNext:在最后一个slot后插入一个新的指向object的表项,并返回该表项的slot号码:如果object为空,会返回错误信息。
  4. slot :产旬号码为index的slot对应对象名,如果该slot为空,会返回错误信息。
  5. findslot:查询一个object所在的slot号码,如果该object为空,会返回错误信息,如果在表中没有找到该object,会返回-1。
  6. install :将号码为index的slot所指向的对象设为object。
  e.g. Classifier类部分Tcl实例过程的使用方法举例,参见

  ——————————————————————————————————
  set ns [new Simulator] ;#建立一个Simulator对象的实例并把它赋值给变量ns。
  set node [$ns node] ;#新建一个节点并赋值给node。
  set udp0 [new Agent/UDP] ;#新建一个UDP Agent并赋值给udp0。
  set udp1 [new Agent/UDP] ;#新建一个UDP Agent并赋值给udp1。
  set null [new Agent/Null] ;#新建一个UDP Agent并赋值给null。
  $ns attach-agent $node $udp0; #将UDP Agent udp0绑定到node上。
  puts "[[$node set dmux_] slot 0]" ;#查询号码为0的slot对应的对象名并将其输出。
  puts "$udp0" ;#输出udp0的值。
  puts "[[$node set dmux_] findslot $udp0]" ;#查询udp0所在的slot号码并输出。
  puts "[[$node set dmux_] findslot $null]" ;#查询null所在的slot号码,表中没有找到该对象,返回-1,并输出。
  puts "============================================="
  puts "[[$node set dmux_] installNext $udp1]";#在最后一个slot 0后插入一个新的指向对象udp1的表项,返回
  ;#该表项的slot号码1,并输出。
  puts "[[$node set dmux_] slot 1]" ;#查询号码为1的slot对应的对象名并将其输出。
  puts "$udp1" ;#输出udp1的值。
  puts "============================================="
  [$node set dmux_] install 0 $udp1 ;#将号码为0的slot所指向的对象设为udp1。
  puts "[[$node set dmux_] slot 0]" ;#查询号码为0的slot对应的对象名并将其输出。
  puts "============================================="
  puts "[[$node set dmux_] alloc-port $null]" ;#寻找一个空闲的slot并输出。
  [$node set dmux_] clear 0 ;# 将号码为0的slot清空。
  puts "[[$node set dmux_] alloc-port $null]" ;#寻找一个空闲的slot并输出。
  puts "============================================="
  puts "[[$node set dmux_] slot 0]" ;# 查询号码为0的slot对应的对象名,该object为空,故返回错误信息。
  $ns run
  运行结果:
  ——————————————————————————————————
  相关Tcl命令:

     1. set $node [$ns node]:建立一个节点实例。
  2. $node id:返回该节点id。
  3. $node neighbors:返回邻居节点的列表。
  4. $node add-neighbor <neighbor_node>:增加一个邻居节点(注:这是单向的邻居,即“neighbor node”是node的邻居,但node不是“neighbor node”的邻居)。
  5. $node node-addr:返回节点的地址(address)。地址类型为def时,节点地址与节点id相同;地址类型为hierarchical时,节点地址是一个字符串。
  6. $node reset:重置连到这个node上的所有agent。
  7. $node agent <port_num> :返回port_num端口所指向的agent对象,如果port_num端口没有指向任何对象,返回null字符串。
  8. $node attach <agent> <optional:port_num> :将agent对象连接到节点上,如果没有指定端口号,节点会自己分配一个空闲的端口,并把agent连接到该端口上;如果指定端口为port_num,节点会把agent连接到端口port_num上。
  9. $node detach <agent> <null_agent>:将agent与节点分离,并把一个null_agent连接到agent原来所在的端口上。
    10. $node incr-rtgtable-size对象变量rtsize_用于保持对每个节点路由表大小的记录,该命令用于当每次路由入口加入分类器时增加路由表的大小。
     11.$node add-route <destination_id> <target>用于增加单播路由的分类器,目标是一个Tcl对象,如果<destination_id>和节点的id相同,则该对象可能就是dmux_的入口(节点的端口复用器)。否则它通常是该目的链表的头。
      12.$ns_ node [<hier_addr>]创建和返回一个节点的实例。若<hier_addr>给出,指派节点地址为<hier_addr>类型。注意:当层次地址通过set-address-format hierarchical{}或者node-config-addressType hierarchical{}调用时,<hier_addr>必须被使用。
原创粉丝点击