ns-2与TCP拥塞模拟 之七 ns-2中自带的应用Ping解读



ns-2TCP拥塞模拟 之七   ns-2中自带的应用Ping解读

ns-2中自带的应用Agent/Ping为例,描述一下如何编写一个“应用”。下面引用的是版本2.35中的代码,其他版本可能略有差异。以下是需要注意的地方:

1)头文件.h中声明的类属性和方法;

2Tcl脚本的变量与C++类的变量的绑定;

3C++类中的command函数是Agent类与Tcl的接口(Tcl脚本的命令直接作用于该函数);


4C++类中的recv函数,是Agent类功能实现的关键,网络中对于数据包的分类、转发和处理的操作都是通过这个函数来实现的;

5TclClass的编写与修改,为Tcl脚本提供接口。

具体的实践过程可以按照以下4个主要步骤进行,举例如下。

1)添加头文件ns-2.35/apps/ping.h

/*

 * File:Header File for a new 'Ping' Agent Class for the ns

 *       network simulator

 *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 {   

    charret;                                  //从源端出来时值为 0,从目的端回来时值为 1

    doublesend_time;                  //源端发送的时间戳,用于往返时延的计算;

    doublercv_time;                      //接收时刻

    intseq;                                     //序列号

 

 

    //Header access methods

    staticint offset_;                      //偏移量

    inlinestatic int& offset() { return offset_; }

    inlinestatic hdr_ping* access(const Packet* p) {

        return(hdr_ping*) p->access(offset_);

    }

};

 

class PingAgent : public Agent {

public:

    PingAgent();

    intseq;                                    //发送序号

    intoneway;                             //标志位

    virtualint command(int argc, const char*const* argv);

    virtualvoid recv(Packet*, Handler*);

};

 

#endif // ns_ping_h

2)添加代码文件ns-2.35/apps/ping.cc

/*

 * File:Code for a new 'Ping' Agent Class for the ns

 *       network simulator

 *Author: Marc Greis (greis@cs.uni-bonn.de), May 1998

*/

 

#include "ping.h"

 

int hdr_ping::offset_;

//以下的两个静态类定义主要完成C++OTcl的连接

static class PingHeaderClass : publicPacketHeaderClass {

public:   

    PingHeaderClass(): PacketHeaderClass("PacketHeader/Ping",

                                                 sizeof(hdr_ping)) {

        bind_offset(&hdr_ping::offset_);

    }

} 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),seq(0), oneway(0)          //PingAgent的构造函数

{

    bind("packetSize_",&size_);

}

 

int PingAgent::command(int argc, constchar*const* argv)

{

  if(argc == 2) {

    if(strcmp(argv[1], "send") == 0) { //如:$p0 send,作为command 函数的输入

      //Create a new packet

     Packet* pkt = allocpkt();

      //Access the Ping header for the new packet:

     hdr_ping* hdr = hdr_ping::access(pkt);

      //Set the 'ret' field to 0, so the receiving node

      //knows that it has to generate an echo packet

      hdr->ret = 0;

      hdr->seq = seq++;

      // Store thecurrent 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);

   

    }

   

    elseif (strcmp(argv[1], "start-WL-brdcast") == 0) {

     Packet* pkt = allocpkt();

     

     hdr_ip* iph = HDR_IP(pkt);

     hdr_ping* ph = hdr_ping::access(pkt);

     

      iph->daddr() = IP_BROADCAST;

     iph->dport() = iph->sport();

      ph->ret = 0;

      send(pkt, (Handler*) 0);

      return (TCL_OK);

    }

 

    elseif (strcmp(argv[1], "oneway") == 0) {

     oneway=1;

     return (TCL_OK);

    }

  }

 

  // Ifthe command hasn't been processed by PingAgent()::command,

  //call the command() function for the base class

  return(Agent::command(argc, argv));

}

 

 

void PingAgent::recv(Packet* pkt, Handler*)

{

  //Access the IP header for the received packet:

  hdr_ip*hdrip = hdr_ip::access(pkt);

 

  //Access the Ping header for the received packet:

 hdr_ping* hdr = hdr_ping::access(pkt);

 

 

  //check if in brdcast mode

  if((u_int32_t)hdrip->daddr() == IP_BROADCAST) {

    if(hdr->ret == 0) {

     

     printf("Recv BRDCAST Ping REQ : at %d.%d from %d.%d\n",here_.addr_, here_.port_, hdrip->saddr(), hdrip->sport());

     Packet::free(pkt);

     

      //create reply

     Packet* pktret = allocpkt();

 

     hdr_ping* hdrret = hdr_ping::access(pktret);

      hdr_ip* ipret = hdr_ip::access(pktret);

     

     hdrret->ret = 1;

     

      //add brdcast address

     ipret->daddr() = IP_BROADCAST;

      ipret->dport() = ipret->sport();

 

      send(pktret, 0);

   

    } else {

     printf("Recv BRDCAST Ping REPLY : at %d.%d from %d.%d\n",here_.addr_, here_.port_, hdrip->saddr(), hdrip->sport());

     Packet::free(pkt);

    }

   return;

  }

  // Isthe '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;

    intrcv_seq = hdr->seq;

    //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::access(pktret);

    //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;

    //Added by Andrei Gurtov for one-way delay measurement.

   hdrret->rcv_time = Scheduler::instance().clock();

   hdrret->seq = rcv_seq;

    //Send the packet

   send(pktret, 0);

  } else{

    // Apacket 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.

    charout[100];

    //Prepare the output to the Tcl interpreter. Calculate the

    //round trip time

    if(oneway) //AG

        sprintf(out,"%s recv %d %d %3.1f %3.1f", name(),

        hdrip->src_.addr_ >>Address::instance().NodeShift_[1],

        hdr->seq, (hdr->rcv_time -hdr->send_time) * 1000,

        (Scheduler::instance().clock()-hdr->rcv_time)* 1000);

    elsesprintf(out, "%s recv %d %3.1f",name(),

        hdrip->src_.addr_ >>Address::instance().NodeShift_[1],

        (Scheduler::instance().clock()-hdr->send_time)* 1000);

   Tcl& tcl = Tcl::instance();

    tcl.eval(out);

    //Discard the packet

   Packet::free(pkt);

  }

}

以上代码的逻辑比较清晰明了,可以看出,比较重要的是command函数中处理“send”命令的部分和recv函数。

函数command中处理“send”命令的部分,在逻辑上是发送端,发送第一个报文分组。

函数recv中结构“if(hdr->ret == 0) {…}”部分,逻辑上是接收端处理收到的分组;填充了一个新的报文分组的标志、时间戳、序号等部分后,发送了这个响应包;当然,这个recv函数比较复杂的部分是考虑了广播包和双向反馈。

3ns-2中应该做的其他必要修改。

修改ns-2.35/common/packet.h文件。

// RAP packets

static const packet_t PT_RAP_DATA = 40;

static const packet_t PT_RAP_ACK = 41;

 

static const packet_t PT_TFRC = 42;

static const packet_t PT_TFRC_ACK = 43;

static const packet_t PT_PING = 44;

       

static const packet_t PT_PBC = 45;

       // Diffusion packets - Chalermek

static const packet_t PT_DIFF = 46;

class p_info {

   

    staticvoid initName()

    {

        if(nPkt_>= PT_NTYPE+1)

                return;

        name_[PT_TFRC]="tcpFriend";

        name_[PT_TFRC_ACK]="tcpFriendCtl";

        name_[PT_PING]="ping";

        

        name_[PT_PBC]= "PBC";

    }

}

修改ns-2.35/tcl/lib/ns-default.tcl文件。

##Agent set seqno_ 0 now is gone

##Agent set class_ 0 now is gone

 

Agent/Ping set packetSize_ 64

 

Agent/UDP set packetSize_ 1000

Agent/UDP instproc done {} { }

Agent/UDP instproc process_data {from data} {}

修改ns-2.35/tcl/lib/ns-packet.tcl文件。

set protolist {

# Application-Layer Protocols:

    Message # a protocol to carry text messages

    Ping # Ping

    PBC # PBC

}


修改ns-2.35/Makefile文件。

OBJ_CC = \   

    tools/random.otools/rng.o tools/ranvar.o common/misc.o common/timer-handler.o \

    tcp/tfrc.otcp/tfrc-sink.o mobile/energy-model.o apps/ping.o tcp/tcp-rfc793edu.o \

执行make 命令。

4)运行以下ping.tcl 进行测试。

#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

 

#Create two ping agents and attach them to thenodes 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 "$p0send"

$ns at 0.4 "$p1send"

$ns at 0.6 "$p0send"

$ns at 0.6 "$p1send"

$ns at 1.0 "finish"

 

#Run the simulation

$ns run

整体上来看,与代理模块相比,添加一个新的“应用”,需要在合适的Tcl文件中添加相应的报文类型定义。

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值