這個章節最主要的目的就是希望使用者可以學會如何新增或修改ns2的核心模組,更明確的說就是去新增和修改[*.cc, *.h]檔案,以筆者和筆者朋友學習ns2的經驗來說,這需要花很多時間和很大的勇氣,時間是花來找資料,勇氣是用來承受當程式寫不好時,ns2可能隨時會當掉。不過別怕,只要跟著筆者所介紹的方法,一定可以成功,若是不成功,就寫信給筆者吧。 若是一開始就要叫ns2的新手去增加的模組,新手一定會哇哇叫,所以筆者不會這樣做,筆者先教大家拷貝ns2已經有的模組檔,但改成別的名字,這樣就可以輕鬆且很明確知道該如何增加模組。
(1) 打開cygwin的命令視窗,把路徑切換到queue的目錄下。 cd ns-allinone-2.27/ns-2.27/queue
(2) 拷貝drop-tail.[cc, h]到myfifo.[cc.h]。 cp drop-tail.cc myfifo.cc cp drop-tail.h myfifo.h
(3) 使用文字編輯軟體去修改myfifo.h和myfifo.cc。(因為我們的環境是在windows下,所以建議可以使用ultra-edit來修改。 a. 先修改myfifo.h,使用取代的功能把所有DropTail改成myfifo,另外,把drop_tail改成myfifo。 #ifndef ns_myfifo_h #define ns_myfifo_h
#include <string.h> #include "queue.h" #include "config.h"
/* * A bounded, drop-tail queue */ class myfifo : public Queue { public: myfifo() { q_ = new PacketQueue; pq_ = q_; bind_bool("drop_front_", &drop_front_); bind_bool("summarystats_", &summarystats); bind_bool("queue_in_bytes_", &qib_); // boolean: q in bytes? bind("mean_pktsize_", &mean_pktsize_); // _RENAMED("drop-front_", "drop_front_"); } ~myfifo() { delete q_; } protected: void reset(); int command(int argc, const char*const* argv); void enque(Packet*); Packet* deque(); void shrink_queue(); // To shrink queue and drop excessive packets. PacketQueue *q_; /* underlying FIFO queue */ int drop_front_; /* drop-from-front (rather than from tail) */ int summarystats; void print_summarystats(); int qib_; /* bool: queue measured in bytes? */ int mean_pktsize_; /* configured mean packet size in bytes */ };
#endif
b. 再修改myfifo.cc,使用取代的功能把所有DropTail改成myfifo,另外,把drop_tail改成myfifo和drop-tail改成myfifo。 #include "myfifo.h"
static class myfifoClass : public TclClass { public: myfifoClass() : TclClass("Queue/myfifo") {} TclObject* create(int, const char*const*) { return (new myfifo); } } class_myfifo;
void myfifo::reset() { Queue::reset(); }
int myfifo::command(int argc, const char*const* argv) { if (argc==2) { if (strcmp(argv[1], "printstats") == 0) { print_summarystats(); return (TCL_OK); } if (strcmp(argv[1], "shrink-queue") == 0) { shrink_queue(); return (TCL_OK); } } if (argc == 3) { if (!strcmp(argv[1], "packetqueue-attach")) { delete q_; if (!(q_ = (PacketQueue*) TclObject::lookup(argv[2]))) return (TCL_ERROR); else { pq_ = q_; return (TCL_OK); } } } return Queue::command(argc, argv); }
/* * drop-tail */ void myfifo::enque(Packet* p) { if (summarystats) { Queue::updateStats(qib_?q_->byteLength():q_->length()); }
int qlimBytes = qlim_ * mean_pktsize_; if ((!qib_ && (q_->length() + 1) >= qlim_) || (qib_ && (q_->byteLength() + hdr_cmn::access(p)->size()) >= qlimBytes)){ // if the queue would overflow if we added this packet... if (drop_front_) { /* remove from head of queue */ q_->enque(p); Packet *pp = q_->deque(); drop(pp); } else { drop(p); } } else { q_->enque(p); } }
//AG if queue size changes, we drop excessive packets... void myfifo::shrink_queue() { int qlimBytes = qlim_ * mean_pktsize_; if (debug_) printf("shrink-queue: time %5.2f qlen %d, qlim %d\n", Scheduler::instance().clock(), q_->length(), qlim_); while ((!qib_ && q_->length() > qlim_) || (qib_ && q_->byteLength() > qlimBytes)) { if (drop_front_) { /* remove from head of queue */ Packet *pp = q_->deque(); drop(pp); } else { Packet *pp = q_->tail(); q_->remove(pp); drop(pp); } } }
Packet* myfifo::deque() { if (summarystats && &Scheduler::instance() != NULL) { Queue::updateStats(qib_?q_->byteLength():q_->length()); } return q_->deque(); }
void myfifo::print_summarystats() { //double now = Scheduler::instance().clock(); printf("True average queue: %5.3f", true_ave_); if (qib_) printf(" (in bytes)"); printf(" time: %5.3f\n", total_time_); }
(4) 修改ns-default.tcl檔,設定初始內定值。 a. cd ns-allinone-2.27/ns-2.27/tcl/lib/ b. 使用文字編輯軟體打開ns-default.tcl c. 使用搜尋的功能找到Queue/DropTail d. 把每個初始設定值都再設一份給Queue/myfifo …………………………………………….. Queue/DropTail set drop_front_ false Queue/DropTail set summarystats_ false Queue/DropTail set queue_in_bytes_ false Queue/DropTail set mean_pktsize_ 500
Queue/myfifo set drop_front_ false Queue/myfifo set summarystats_ false Queue/myfifo set queue_in_bytes_ false Queue/myfifo set mean_pktsize_ 500 ……………………………………………..
(5) 修改Makefile,把myfifo.o加入OBJ_CC內,並重新編譯。 a. 使用文字編輯軟體打開ns-allinone-2.27/ns-2.27目錄下的Makefile b. 使用搜尋找到drop-tail.o。 c. 在drop-tail.o後面加上queue/myfifo。 …………………………………………….. tools/flowmon.o tools/loss-monitor.o \ queue/queue.o queue/drop-tail.o queue/myfifo.o \ adc/simple-intserv-sched.o queue/red.o \ …………………………………………… d. 重新編譯。 make
若是沒有什麼錯誤訊息產生,就是編譯成功。如下圖所示。
(6) 測試一下新安裝的模組是否可以正常的運作。可以把修改基本工具章節中的範例,把DropTail改成myfifo,試看看跑出來的結果是否跟用DropTail跑出來的一樣。
在這邊先做個小結,如何新增模組到ns2的核心步驟如下: 1.準備好模組檔(例如,a.cc 和 a.h)。 2.若有需要做初始設定的話,修改ns-default.tcl檔。 3.修改Makefile(把a.o加到OBJ_CC內) 4.重新編譯 5.測試模組
關於如何去寫C++的模組檔案,並不包含在這份文件內。因為每個模組的功能都不相同,但筆者可以給使用者一些建議,因為ns2內已經有很多類型的模組檔案,使用者可以根據不同的需要,找到最相近的模組檔,然後再去修改,以這樣的方式會比較容易把自己所需要的模組加入ns2的核心。
繼續新的主題前,筆者要告訴使用者在C++程式內非常重要的地方,第一個是bind相關函數,以前面的myfifo為例,在myfifo.h程式碼內: class myfifo : public Queue { public: myfifo() { q_ = new PacketQueue; pq_ = q_; bind_bool("drop_front_", &drop_front_); bind_bool("summarystats_", &summarystats); bind_bool("queue_in_bytes_", &qib_); // boolean: q in bytes? bind("mean_pktsize_", &mean_pktsize_); // _RENAMED("drop-front_", "drop_front_"); } …………………………………………….. 可以看到bind或者是bind_bool,這兩個函數主要的目的是要把TCL的變數和C++的變數產生互連的關係,這樣使用者就可以在TCL檔內去設定或者去查看目前C++的變數值。(bind_bool是用在布林變數,bind是用在一般實數;而第一個參數是TCL的變數,第二個參數是C++內的變數。) 筆者以在基本工具內的範例為例,教大家如何去改變或查看C++的變數值。
[範例一:設定C++的參數值] 一般而言,FiFo queue的行為都是當佇列內的暫存區滿了以後,就會把新進的封包所丟棄,這是因為在ns-default.tcl初始設定檔內 drop_front_是設成false。但是如果想要改變成當暫存區滿了時候,要把佇列暫存區最前面的封包所丟棄,則可以在TCL內使用:
#以n2到n3之間的的佇列為例 set qn2n3_ [[$ns link $n2 $n3] queue] $qn2n3_ set drop_front_ true
[範例二:如何去查看即時C++的參數值] 筆者以基本工具篇的範例為例,教大家如何量測FTP那條TCP flow的congestion window變化。 # 產生一個模擬的物件 set ns [new Simulator]
#針對不同的資料流定義不同的顏色,這是要給NAM用的 $ns color 1 Blue $ns color 2 Red
#開啟一個NAM trace file set nf [open out.nam w] $ns namtrace-all $nf
#開啟一個trace file,用來記錄封包傳送的過程 set nd [open out.tr w] $ns trace-all $nd
#開啟一個檔案,用來記錄TCP Flow的congestion window變化情況 set wnd_trace [open cwnd.tr w]
#定義一個紀錄的程序,每隔0.1秒就去查看目前TCP Flow的值 proc record {} { global ns tcp wnd_trace set time 0.1
#讀取C++內cwnd_的變數值 set curr_cwnd [$tcp set cwnd_] set now [$ns now] puts $wnd_trace "$now $ curr_cwnd " $ns at [expr $now+$time] "record" }
#定義一個結束的程序 proc finish {} { global ns nf nd wnd_trace $ns flush-trace close $nf close $nd close $wnd_trace #以背景執行的方式去執行NAM #exec nam out.nam & exit 0 }
#產生四個網路節點 set n0 [$ns node] set n1 [$ns node] set n2 [$ns node] set n3 [$ns node]
#把節點連接起來 $ns duplex-link $n0 $n2 2Mb 10ms DropTail $ns duplex-link $n1 $n2 2Mb 10ms DropTail $ns duplex-link $n2 $n3 1.7Mb 20ms DropTail
set qn2n3_ [[$ns link $n2 $n3] queue]
#設定ns2到n3之間的Queue Size為10個封包大小 $ns queue-limit $n2 $n3 10
#設定節點的位置,這是要給NAM用的 $ns duplex-link-op $n0 $n2 orient right-down $ns duplex-link-op $n1 $n2 orient right-up $ns duplex-link-op $n2 $n3 orient right
#觀測n2到n3之間queue的變化,這是要給NAM用的 $ns duplex-link-op $n2 $n3 queuePos 0.5
#建立一條TCP的連線 set tcp [new Agent/TCP] $tcp set class_ 2 $ns attach-agent $n0 $tcp set sink [new Agent/TCPSink] $ns attach-agent $n3 $sink $ns connect $tcp $sink #在NAM中,TCP的連線會以藍色表示 $tcp set fid_ 1
#在TCP連線之上建立FTP應用程式 set ftp [new Application/FTP] $ftp attach-agent $tcp $ftp set type_ FTP
#建立一條UDP的連線 set udp [new Agent/UDP] $ns attach-agent $n1 $udp set null [new Agent/Null] $ns attach-agent $n3 $null $ns connect $udp $null #在NAM中,UDP的連線會以紅色表示 $udp set fid_ 2
#在UDP連線之上建立CBR應用程式 set cbr [new Application/Traffic/CBR] $cbr attach-agent $udp $cbr set type_ CBR $cbr set packet_size_ 1000 $cbr set rate_ 1mb $cbr set random_ false
#設定FTP和CBR資料傳送開始和結束時間 $ns at 0.1 "$cbr start" $ns at 1.0 "$ftp start" $ns at 1.1 "record" $ns at 4.0 "$ftp stop" $ns at 4.5 "$cbr stop"
#結束TCP的連線(不一定需要寫下面的程式碼來實際結束連線) $ns at 4.5 "$ns detach-agent $n0 $tcp ; $ns detach-agent $n3 $sink"
#在模擬環境中,5秒後去呼叫finish來結束模擬(這樣要注意模擬環境中 #的5秒並不一定等於實際模擬的時間 $ns at 5.0 "finish"
#執行模擬 $ns run
然後使用gnuplot,設定環境變數 set xtics 1,0.2,4 set xrange [1:4] plot “cwnd.tr” with lines
第二個要注意的地方是在C++程式碼内的command程序,這個command程序可以讓TCL去呼叫C++的命令。 例如$ftp start、$ftp stop中的”start”和”stop”都是去呼叫C++模組內的命令。所以使用者若是在寫C++模組程式時,要提供一些程序給TCL用,就可以寫在command內。
最後,在結束這個章節前,筆者要跟大家做個經驗的分享。在筆者使用ns2去做研究的時候,經常會去用myfifo queue去做一些量測,例如去量測封包傳送到網路的時的時間、大小、或者是統計封包被丟棄的數量,雖然這些量測都可以使用awk去分析trace file而得到,但是要提醒大家,若是要模擬的資料太大的時候,去分析的時間也會很久,但若是直接在模組內去撰寫量測的程式,模擬結束後,所需要的資料就已經可以獲得了。
|
|