在本小節中,筆者將介紹如何在ns2核心中直接新增模組,以用來量測以UDP為傳輸協定的應用程式之One Way Delay (OWD)IP Delay Variance (IPDV,或是Jitter)Packet Loss數量。使用這個方法的好處就是當模擬結束後,所需要的數據就直接存放到檔案內,然後就可以直接進行分析。

        我們要採用的方法就是在封包的接收端去查看封包表頭內的資訊以計算出OWDIPDV、或者是Packet Loss數量,其中最重要的資訊就是Sending Time(傳送時間)Sequence Number(封包序號)Sending Time可以用來計算出OWDIPDV,計算的方式如下:

 

One Way Delay = 接收時間 傳送時間

IPDV = |目前量測到的OWD 上一次量測到的OWD |

 

        Packet Loss數量則可以由封包序號的不連續性所計算出,每一個封包在傳送端發送前,傳送端都會給封包一個序號,序號是連續性的,因此若是在接收到發現序號有不連續的發生,則可視為有封包的移失(這邊不考慮Out-of-Order情況)

       

[方法]

1.      NS2中,在RTP表頭中已經有序號的欄位。目前第一步要做的就是去增加一個Sending Time的欄位,並且當封包在傳送端發送時,把發送時間紀錄到此欄位內。

 

1.1    新增欄位sendtime_packet common header (修改common/packet.h)

struct hdr_cmn {

        enum dir_t { DOWN= -1, NONE= 0, UP= 1 };

        packet_t ptype_;      // packet type (see above)

        int     size_;                // simulated packet size

        int     uid_;         // unique id

        int     error_;              // error flag

        int     errbitcnt_;     // # of corrupted bits jahn

        int     fecsize_;

        double      ts_;           // timestamp: for q-delay measurement

        int     iface_;              // receiving interface (label)

        dir_t direction_;        // direction: 0=none, 1=up, -1=down

        double  sendtime_;  // 新增的sendtime_欄位

        ...

        ...

        ...

        inline int& addr_type() { return (addr_type_); }

        inline int& num_forwards() { return (num_forwards_); }

        inline int& opt_num_forwards() { return (opt_num_forwards_); }

        //monarch_end

        inline double& sendtime() { return (sendtime_); } // added by smallko

   }

 

1.1    udp要傳送封包出去時,把現在時間加入到sendtime這個欄位。(修改apps/udp.cc)

void UdpAgent::sendmsg(int nbytes, AppData* data, const char* flags)

   {

    ...

    ...

    ...

    double local_time = Scheduler::instance().clock();

        while (n-- > 0) {

                p = allocpkt();

                hdr_cmn::access(p)->size() = size_;

                hdr_rtp* rh = hdr_rtp::access(p);

                rh->flags() = 0;

                rh->seqno() = ++seqno_;

                hdr_cmn::access(p)->timestamp() =

                    (u_int32_t)(SAMPLERATE*local_time);

                hdr_cmn::access(p)->sendtime_ = local_time;   //把現在時間加入到sendtime這個欄位

                // add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)

                if (flags && (0 ==strcmp(flags, "NEW_BURST")))

                        rh->flags() |= RTP_M;

                p->setdata(data);

                target_->recv(p);

        }

        n = nbytes % size_;

        if (n > 0) {

                p = allocpkt();

                hdr_cmn::access(p)->size() = n;

                hdr_rtp* rh = hdr_rtp::access(p);

                rh->flags() = 0;

                rh->seqno() = ++seqno_;

                hdr_cmn::access(p)->timestamp() =

                    (u_int32_t)(SAMPLERATE*local_time);

                hdr_cmn::access(p)->sendtime_ = local_time;   //把現在時間加入到sendtime這個欄位

                // add "beginning of talkspurt" labels (tcl/ex/test-rcvr.tcl)

                if (flags && (0 == strcmp(flags, "NEW_BURST")))

                        rh->flags() |= RTP_M;

                p->setdata(data);

                target_->recv(p);

        }

        idle();

}  

 

2.      tools的目錄內,新增mymeasure.hmymeasure.cc

[mymeasure.h內容]

#include <tclcl.h>

#include <stdio.h>

#include "agent.h"

#include "config.h"

#include "packet.h"

#include "ip.h"

#include "rtp.h"

 

class mymeasure : public Agent {

public:

        mymeasure();

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

        virtual void recv(Packet* pkt, Handler*);

protected:

        int nlost_;       //移失封包數量

        int npkts_;             //接收到的封包數量

        int expected_;

        int bytes_;      //接收到的封包總長度(bytes)

        int seqno_;             //封包序號

        double last_packet_time_; //上一次封包接收時間

       

        char buf[100];

        FILE *pFile;          

        double now;

        double sum_ipdv_;     //jitter總和,可以用來算average jitter

        double sum_owd_;      //one way delay總和,可以用來算average owd            

        double current_owd_;  //目前所量測到的one way delay

        double current_ipdv_; //目前所量測到的jitter

        double min_owd_;      //最小的one way delay

        double previous_owd_; //上一次量測到的one way delay

};

 

[mymeasure.cc內容]

#include <tclcl.h>

#include "agent.h"

#include "config.h"

#include "packet.h"

#include "ip.h"

#include "rtp.h"

#include "mymeasure.h"

 

static class mymeasureClass : public TclClass {

public:

        mymeasureClass() : TclClass("Agent/mymeasure") {}

        TclObject* create(int, const char*const*) {

                return (new mymeasure());

        }

} class_mymeasure;

 

mymeasure::mymeasure() : Agent(PT_NTYPE)

{

        bytes_ = 0;

        nlost_ = 0;

        npkts_ = 0;

        expected_ = -1;

        last_packet_time_ = 0.;

        seqno_ = 0;

       

        sum_ipdv_                 = 0;             

        sum_owd_                    = 0;     

        current_owd_             = 0;     

        current_ipdv_             = 0;     

        previous_owd_           = 0;

        min_owd_                  = 100000000;

       

        bind("nlost_", &nlost_);

        bind("npkts_", &npkts_);

        bind("bytes_", &bytes_);

        bind("lastPktTime_", &last_packet_time_);

        bind("expected_", &expected_);

}

 

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

{

        hdr_rtp* p = hdr_rtp::access(pkt);

        seqno_ = p->seqno();

        bytes_ += hdr_cmn::access(pkt)->size();

        ++npkts_;

       

        now=Scheduler::instance().clock();

       

        //計算One Way Delay

        current_owd_=(Scheduler::instance().clock()-hdr_cmn::access(pkt)->sendtime_);

       

        //看看量測到的owd是否是最小值

        if (current_owd_<min_owd_)

                min_owd_ = current_owd_;

       

        sum_owd_+=current_owd_;     // one way delay總和,可以用來算average owd

       

        if (previous_owd_!=0)      {   // Jitter: ipdv=|current_owd_-previous_owd_|

                if (previous_owd_<current_owd_)

                        current_ipdv_=(current_owd_-previous_owd_);

                else

                        current_ipdv_=(previous_owd_-current_owd_);

               

                sum_ipdv_+=current_ipdv_; // ipdv總和,可以用來算average ipdv (Jitter)

        }

        previous_owd_=current_owd_;

       

        //在這裡,只把one way delay的資訊寫到檔案內.若是有不同的需要,可以參照下面這行程式

        //把所需的資料寫到檔案

        fprintf(pFile, "%0.1f %0.1f\n", now * 1000, current_owd_ * 1000);

               

        //檢查封包是否有移失

        if (expected_ >= 0) {

                //從封包序號的不連續性,就可以得知是否有封包移失

                int loss = seqno_ - expected_;

                if (loss > 0) {

                        nlost_ += loss;

                        Tcl::instance().evalf("%s log-loss", name());

                }

        }

        last_packet_time_ = Scheduler::instance().clock();

        expected_ = seqno_ + 1;

        Packet::free(pkt);

}

 

int mymeasure::command(int argc, const char*const* argv)

{

        if (strcmp(argv[1], "clear") == 0) {

                expected_ = -1;

                return (TCL_OK);

        }

       

        //設定檔名

        if (strcmp(argv[1], "set_filename") == 0) {

                strcpy(buf, argv[2]);

                pFile = fopen(buf, "w");

                return (TCL_OK);

        } 

       

        //關閉檔案

        if (strcmp(argv[1], "close_file") == 0) {

                fclose(pFile);

                return (TCL_OK);

        } 

       

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

}

 

3. 修改ns-default.tcl檔,把下面的程式碼加入。

Agent/mymeasure set nlost_ 0

Agent/mymeasure set npkts_ 0

Agent/mymeasure set bytes_ 0

Agent/mymeasure set lastPktTime_ 0

Agent/mymeasure set expected_ 0

 

4. Makefile內把tools/mymeasure.o加入OBJ_CC內。

5. 重新Make

6. 測試。下面的程式碼是用來測試One Way Delay

set ns [new Simulator]

 

set testTime   85.0

 

set s [$ns node]

set d [$ns node]

 

$ns duplex-link $s $d 10Mb 5.5ms DropTail

 

set udp [new Agent/UDP]

$ns attach-agent $s $udp

set cbr [new Application/Traffic/CBR]

$cbr attach-agent $udp

$cbr set packet_size_ 1000

$udp set packetSize_ 1000

$cbr set rate_ 1000000

set null [new Agent/mymeasure]

$ns attach-agent $d $null

$ns connect $udp $null

$null set_filename owd_flow0

 

proc finish {} {

    global ns

    exit 0

}

 

$ns at 0.0 "$cbr start"

$ns at $testTime "$cbr stop"

$ns at $testTime "$null close_file"

$ns at [expr $testTime + 1.0] "finish"

 

$ns run

 

執行結束後,會產生一個檔案owd_flow0,裡面紀錄了每封包的one-way-delay

6.3 6.3

14.3 6.3

22.3 6.3

30.3 6.3

38.3 6.3

46.3 6.3

54.3 6.3

62.3 6.3

70.3 6.3

78.3 6.3

……

……

……

由於測試的程式是CBR,所以所有的數據都是6.3ms(包含了5.5mspropagation delay和封包被節點處理時間)