创建博客 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

冷的博客

虽知无用,而未能忘情,到底是热肠挂住;虽不能忘情,而终不下手,到底是冷眼看穿。

 
 
 

日志

 
 

NS2.35 Ping协议的实现  

2013-01-29 11:43:29|  分类: ns2相关 |  标签: |举报 |字号 订阅

使用ubuntu的第二天,继续先前在windows下进行的ns学习。主要是参考Marc Greis' Tutorial 的第七部分VII. A new protocol for NS,实现一个类似ping 请求的协议,即发送结点发送包并记录发送时间(时间戳),接收结点收到包后立即返回,以便测量往返所需时间。实现的过程中发现ns2.35版本已经包含了这个协议,所以实现变成了验证和对比,最后记录总结如下:

在NS2下实现一个协议,主要是编写.h (头文件)和 .cc(C++文件) 两个文件,头文件主要是类定义, 而 .cc文件,需要完成以下工作:
    (1). TclClass的派生类的编写,为TCL脚本提供接口, 此类的代码基本不变,每次只做简单的替换即可。
    (2). TCL脚本的变量和 C++ 中类的变量的绑定函数(bind),也只直接对应着填写即可。
    (3). .h中定义的类的构造方法的实现。
    (4). command 函数,是Agent类与TCL的接口,TCL脚本的命令直接作用于该函数。
    (5). recv函数,是Agent类功能实现的关键; 网络中对于数据包的分类、转发和处理的操作都是通过这个函数来实现的。
在~ns-2.35/apps 路径下可以找到ping.h和ping.cc这两个文件,其主要内容与Marc Greis' Tutorial中的ping.h是一致的,但在2.35版本中扩充了IP广播等功能,本文的.h和.cc文件是Marc Greis' Tutorial中的代码,比较易于理解。

1.  ping.h 文件:
/* File: Header File for a new 'Ping' Agent Class for NS
 * Author: Marc Greis (greis@cs.uni-bonn.de ), May 1998
 */
#ifndef ns_ping_h   //采用条件编译,防止重复编译
#define ns_ping_h

#include "agent.h"  //包含需要的头文件
#include "tclcl.h"
#include "packet.h"
#include "address.h"
#include "ip.h"

struct hdr_ping {   //ping包头部的数据结构
  char ret;         //从源端出来时值为 0, 从目的端回来时值为 1;
  double send_time; //源端发送的时间戳,用于往返时延的计算;
};
class PingAgent : public Agent {
 public:
  PingAgent();
  int command(int argc, const char*const* argv);
  void recv(Packet*, Handler*);
 protected:
  int off_ping_;  //it will be used to access a packet's ping header
};

#endif

2.  ping.cc 文件:“//”后面的内容为注释,非常详细

/* File: Code for a new 'Ping' Agent Class for NS
 * Author: Marc Greis (greis@cs.uni-bonn.de ), May 1998
 */

#include "ping.h"

//以下的两个函数主要完成C++和OTCL的连接,每次可套用,做相应的修改即可!

static class PingHeaderClass : public PacketHeaderClass {
public:
  PingHeaderClass() : PacketHeaderClass("PacketHeader/Ping",
     sizeof(hdr_ping)) {}
} class_pinghdr;

static class PingClass : public TclClass {
public:
  PingClass() : TclClass("Agent/Ping") {}
  TclObject* create(int, const char*const*) {
    return (new PingAgent());
  }
} class_ping;

PingAgent::PingAgent() : Agent(PT_PING)   //PingAgent的构造函数
{
  bind("packetSize_", &size_);     //变量绑定
  bind("off_ping_", &off_ping_);
}

/×  The function 'command()' is called when a Tcl command for the   class 'PingAgent' is executed.    In our case that would be '$pa send' (assuming 'pa' is an instance of the Agent/Ping class), because we want to send ping packets from the Agent to another ping agent. You basically have to parse the command in the 'command()' function, and if no match is found, you have to pass the command with its arguments to the 'command()' function of the base class (in this case 'Agent::command()').  ×/

// $pa send 命令作为command 函数的输入

int PingAgent::command(int argc, const char*const* argv)
{
  if (argc == 2) {
    if (strcmp(argv[1], "send") == 0) {
      // Create a new packet
      Packet* pkt = allocpkt();
      // Access the Ping header for the new packet:
      hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
      // Set the 'ret' field to 0, so the receiving node knows
      // that it has to generate an echo packet
      hdr->ret = 0;
      // Store the current time in the 'send_time' field
      hdr->send_time = Scheduler::instance().clock();
      // Send the packet
      send(pkt, 0);
      // return TCL_OK, so the calling function knows that the
      // command has been processed
      return (TCL_OK);
    }
  }
  // If the command hasn't been processed by PingAgent()::command,
  // call the command() function for the base class
  return (Agent::command(argc, argv));
}

/×  The function 'recv()' defines the actions to be taken when a packet is received. If the 'ret' field is 0, a packet with the same value for the 'send_time' field, but with the 'ret' field set to 1 has to be returned. If 'ret' is 1, a Tcl function (which has to be defined by the user in Tcl) is called and processed the event. */

void PingAgent::recv(Packet* pkt, Handler*)  //recv函数,Agent类功能实现的关键
{
  // Access the IP header for the received packet:
  hdr_ip* hdrip = (hdr_ip*)pkt->access(off_ip_);
  // Access the Ping header for the received packet:
  hdr_ping* hdr = (hdr_ping*)pkt->access(off_ping_);
  // Is the 'ret' field = 0 (i.e. the receiving node is being pinged)?
  if (hdr->ret == 0) {
    // Send an 'echo'. First save the old packet's send_time
    double stime = hdr->send_time;
    // Discard the packet
    Packet::free(pkt);
    // Create a new packet
    Packet* pktret = allocpkt();
    // Access the Ping header for the new packet:
    hdr_ping* hdrret = (hdr_ping*)pktret->access(off_ping_);
    // Set the 'ret' field to 1, so the receiver won't send another echo
    hdrret->ret = 1;
    // Set the send_time field to the correct value
    hdrret->send_time = stime;
    // Send the packet
    send(pktret, 0);
  } else {
    // A packet was received. Use tcl.eval to call the Tcl
    // interpreter with the ping results.
    // Note: In the Tcl code, a procedure 'Agent/Ping recv {from rtt}'
    // has to be defined which allows the user to react to the ping
    // result.
    char out[100];
    // Prepare the output to the Tcl interpreter. Calculate the round
    // trip time
    sprintf(out, "%s recv %d %3.1f", name(),
            hdrip->src_ >> Address::instance().NodeShift_[1],
     (Scheduler::instance().clock()-hdr->send_time) * 1000);
    Tcl& tcl = Tcl::instance();
    tcl.eval(out);
    // Discard the packet
    Packet::free(pkt);
  }
}

 3. NS2中应该做的必要修改
    (1).修改 "packet.h" 文件(路径为:~ns-2.35/common/)
添加包协议的ID:
         enum packet_t {
              PT_TCP,
              PT_UDP,
              ......
           // insert new packet types here
              PT_TFRC,
              PT_TFRC_ACK,
              PT_PING,    //  packet protocol ID for our ping-agent
              PT_NTYPE    // This MUST be the LAST one
          };
在ns2.35中, packet.h 对 PT_PING 的定义没有采用枚举,而是定义成为了static const packet_t 类型的:
         .......
         static const packet_t PT_TFRC = 42;
         static const packet_t PT_TFRC_ACK = 43;
         static const packet_t PT_PING = 44;
         .......

修改  class p_info {} 结构:
        
         class p_info {
            public:
                  p_info() {
      name_[PT_TCP]= "tcp";
                  name_[PT_UDP]= "udp";
                  ...........
                  name_[PT_TFRC]= "tcpFriend";
                  name_[PT_TFRC_ACK]= "tcpFriendCtl";
                  name_[PT_PING]="Ping";  //添加的

                  name_[PT_NTYPE]= "undefined";
             }
                 .....
            }

在ns2.35中, class p_info{}的结构如下

  class p_info {
               public:
                     p_info() {
                           initName();  //因此我应该到initName()中做修改!!
                     }
               ............
               static void initName()
              {
                    if(nPkt_ >= PT_NTYPE+1)
                            return;
                    char **nameNew = new char*[PT_NTYPE+1];
                    ...............
 
                    name_[PT_TFRC]= "tcpFriend";
                    name_[PT_TFRC_ACK]= "tcpFriendCtl";
                    name_[PT_PING]="ping";  //应该添加!
                    ................
                    ................

              }

(2).编辑ns-2.35/tcl/lib/ns-default.tcl文件,设置Agent/Ping包的缺省大小:

          ##Agent set seqno_ 0 now is gone
          ##Agent set class_ 0 now is gone
 
           Agent/Ping set packetSize_ 64  //设置缺省包大小为64

(3).在文件‘/tcl/lib/ns-packet.tcl’添加新ping包的入口:

        { SRMEXT off_srm_ext_}
                  { Ping off_ping_ }} {
                  set cl PacketHeader/[lindex $pair 0]

在ns2.35版本中

          #   { UMP off_ump_  }
          #   { TFRC off_tfrm_ }
          #   { Ping off_ping_ }  //注意,已经注释掉了这是老版本的代码
          #   { rtProtoLS off_LS_ }
          #   { MPLS off_mpls_ }
          #   { GAF off_gaf_ }
          #   { LDP off_ldp_ }
          #   } {
          #   create-packet-header [lindex $pair 0] [lindex $pair 1]
          #  }

在ns2.34版本中是采用foreach prot {} {} 结构实现的,而在2.35版本中是采用set protolist来实现的,如下:

        set protolist {
                    ................

         # Application-Layer Protocols:
        Message # a protocol to carry text messages
        Ping     # Ping
        PBC     # PBC
                    ................}

(4).最后的改变是‘~/ns-2.XX/Makefile’。需要将‘ping.o’添加到目标文件列表。

          sessionhelper.o delaymodel.o srm-ssm.o /
          srm-topo.o /
          ping.o /
          $(LIB_DIR)int.Vec.o $(LIB_DIR)int.RVec.o /
          $(LIB_DIR)dmalloc_support.o /

在2.35版本中为:

diffusion/hash_table.o diffusion/routing_table.o diffusion/iflist.o \
    tcp/tfrc.o tcp/tfrc-sink.o mobile/energy-model.o apps/ping.o tcp/tcp-rfc793edu.o \
    queue/rio.o queue/semantic-rio.o tcp/tcp-sack-rh.o tcp/scoreboard-rh.o \

接下来只需要将路径切换到makefile文件的目录下,然后执行make即可,编译生成的ping.o文件在ns-2.35/apps/目录下。关于makefile的深入剖析,网上有一篇很好的文章《跟我一起学makefile》,可供参考学习。


4.至此就完成了ping协议的编程,可以像应用tcp协议那样编写tcl脚本应用ping协议了,一个示例如下:

ping.tcl 文件
   #Create a simulator object
set ns [new Simulator]

#Open a trace file
set tracefd [open out.tr w]
$ns trace-all $tracefd
set nf [open out.nam w]
$ns namtrace-all $nf

#Define a 'finish' procedure
proc finish {} {
        global ns nf
        $ns flush-trace
        close $nf
        exec nam out.nam &
        exit 0
}

#Create three nodes
set n0 [$ns node]
set n1 [$ns node]
set n2 [$ns node]

#Connect the nodes with two links
$ns duplex-link $n0 $n1 1Mb 10ms DropTail
$ns duplex-link $n1 $n2 1Mb 10ms DropTail

#Define a 'recv' function for the class 'Agent/Ping'
Agent/Ping instproc recv {from rtt} {
 $self instvar node_
 puts "node [$node_ id] received ping answer from /
              $from with round-trip-time $rtt ms."
}

#Create two ping agents and attach them to the nodes n0 and n2
set p0 [new Agent/Ping]
$ns attach-agent $n0 $p0

set p1 [new Agent/Ping]
$ns attach-agent $n2 $p1

#Connect the two agents
$ns connect $p0 $p1

#Schedule events
$ns at 0.2 "$p0 send"
$ns at 0.4 "$p1 send"
$ns at 0.6 "$p0 send"
$ns at 0.6 "$p1 send"
$ns at 1.0 "finish"

#Run the simulation
$ns run

5.运行结果

运行上述tcl脚本,在我的机子上得到的结果如下:

node 0 received ping answer from  2 with round-trip-time 42.0 ms.
node 2 received ping answer from  0 with round-trip-time 42.0 ms.
node 0 received ping answer from  2 with round-trip-time 42.0 ms.
node 2 received ping answer from  0 with round-trip-time 42.0 ms.
同时出现动画窗口out


6.参考资料

Marc Greis' Tutorial :http://www.isi.edu/nsnam/ns/tutorial/index.html,VII. A new protocol for NS
中文翻译:http://www.cnblogs.com/zhangleiccst/archive/2011/09/04/2166177.html
参考博文:http://blog.csdn.net/kzm2008/article/details/5460160

  评论这张
 
阅读(472)| 评论(0)
|      
推荐 转载

历史上的今天

最近读者

热度

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2014