首先是Scheduler,Handler和Event類的關係。
在NS2中,事件(Event)是基本的調度單元,比如發送一個Packet、接收一個Packet等等。每個Event都有自己的處理工具,這個工具就是一個Handler類的對象handler_。Handler中僅包含一個函數,描述對Event的處理方法,即handle(Event*e)。可以看看NS2中的Handler的源代碼:
class Handler {
public:
virtual ~Handler () {}
virtual void handle(Event* event) = 0;
};
在這裏,Handler只是一個抽象類,只定義了一個虛析構函數和一個純虛函數handle。
給定一個事件,Scheduler將調用(注意是schedule函數而不是Schedule函數)schedule(Handler*h, Event* e, doubledelay)函數,該函數設定Event的uid_,並設定用於處理該事件的Handler:e->handler_=h,然後將該事件插入Scheduler維護的事件隊列中。一般說來,處理事件的Handler都是產生該事件的實體本身,所以我們常常可以看到schedule函數在調用的時候*h用到this指針。
可以看看Scheduler的源代碼:
<span style="font-family:Comic Sans MS;font-size:12px;">void
Scheduler::schedule(Handler* h, Event* e, double delay)
{
// handler should ALWAYS be set... if it's not, it's a bug in the caller
if (!h) {
fprintf(stderr,
"Scheduler: attempt to schedule an event with a NULL handler."
" Don't DO that.\n");
abort();
};
if (e->uid_ > 0) {
printf("Scheduler: Event UID not valid!\n\n");
abort();
}
if (delay < 0) {
// You probably don't want to do this
// (it probably represents a bug in your simulation).
fprintf(stderr,
"warning: ns Scheduler::schedule: scheduling event\n\t"
"with negative delay (%f) at time %f.\n", delay, clock_);
}
if (uid_ < 0) {
fprintf(stderr, "Scheduler: UID space exhausted!\n");
abort();
}
e->uid_ = uid_++;
e->handler_ = h;
double t = clock_ + delay;
e->time_ = t;
insert(e);
}</span>
這裏指明瞭event的uid號(暫時還不知道這個號是幹嗎的),處理這個event的handler以及time,最後將這個事件插入到了事件隊列當中去了(insert函數在Schedule中聲明爲一個虛函數,需要派生類來重寫這個函數)。
————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
再看下TimeHandler類的用法(其實我也不是很明白,先寫着吧~)
1、先看看我從這個網址中截取出來的一些話:http://www.cnblogs.com/zhangleiccst/archive/2011/09/19/2181286.html
定時器可以用來每隔固定時間重複一件事情???做的什麼事情,由誰來做?跟Event和Scheduler又有什麼關係??這些問題先拋在這裏吧。。。
先看看TimeHandler類的源代碼吧~
<span style="font-family:Comic Sans MS;font-size:12px;">class TimerHandler : public Handler {
public:
TimerHandler() : status_(TIMER_IDLE) { }
void sched(double delay); // cannot be pending
void resched(double delay); // may or may not be pending
// if you don't know the pending status
// call resched()
void cancel(); // must be pending
inline void force_cancel() { // cancel!
if (status_ == TIMER_PENDING) {
_cancel();
status_ = TIMER_IDLE;
}
}
enum TimerStatus { TIMER_IDLE, TIMER_PENDING, TIMER_HANDLING };
int status() { return status_; };
protected:
virtual void expire(Event *) = 0; // must be filled in by client
// Should call resched() if it wants to reschedule the interface.
virtual void handle(Event *);
int status_;
Event event_;
private:
inline void _sched(double delay) {
(void)Scheduler::instance().schedule(this, &event_, delay);
}
inline void _cancel() {
(void)Scheduler::instance().cancel(&event_);
// no need to free event_ since it's statically allocated
}
};</span>
這裏的inline void _sched(double delay)函數中調用了schedule函數(schedule函數是幹嗎?就是給事件event指定uid號,handler以及將這個事件插入到隊列當中去)
一般說來,特定的Timer都是給特定的實體來使用的,比如說,TcpAgent中會有特定的Timer(對應的有三種Timer)來分別出來在TCP傳輸過程中所需要的定時器。在這些定時器中一般都會有這個特定的對象,比如說以上所說的TcpAgent中用到的幾個定時器的定義:
<span style="font-size:12px;">class RtxTimer : public TimerHandler {
public:
RtxTimer(TcpAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
TcpAgent *a_;
};
class DelSndTimer : public TimerHandler {
public:
DelSndTimer(TcpAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
TcpAgent *a_;
};
class BurstSndTimer : public TimerHandler {
public:
BurstSndTimer(TcpAgent *a) : TimerHandler() { a_ = a; }
protected:
virtual void expire(Event *e);
TcpAgent *a_;
};</span>
而後在TcpAgent的定義中使用這些Timer的時候,首先是構造函數<span style="font-size:12px;">TcpAgent::TcpAgent()
: Agent(PT_TCP),
t_seqno_(0), t_rtt_(0), t_srtt_(0), t_rttvar_(0),
t_backoff_(0), ts_peer_(0), ts_echo_(0),
tss(NULL), tss_size_(100),
<strong> <span style="color:#FF0000;">rtx_timer_(this), delsnd_timer_(this), burstsnd_timer_(this), </span></strong>
dupacks_(0), curseq_(0), highest_ack_(0), cwnd_(0), ssthresh_(0),
maxseq_(0), count_(0), rtt_active_(0), rtt_seq_(-1), rtt_ts_(0.0),
lastreset_(0.0), closed_(0), use_rtt_(0),
first_decrease_(1), fcnt_(0),
nrexmit_(0), restart_bugfix_(1), cong_action_(0),
ecn_burst_(0), ecn_backoff_(0), ect_(0),
qs_requested_(0), qs_approved_(0),
qs_window_(0), qs_cwnd_(0), frto_(0)</span>
注意到在構造函數中是用this指針來初始化這三個定時器的。那麼這些定時器在調用從父類TimeHandler繼承來的inline void _sched(double delay)函數時則會將處理Event(好吧,貌似還不知道Event是啥,貌似就是隨便一個Event)的Handler設爲自己,也就是對應的各個Timer。
在時間到了之後,這些定時器將會執行handle函數(如何執行不得而知),而handle函數內容是怎樣的呢?
<span style="font-size:12px;">void
TimerHandler::handle(Event *e)
{
if (status_ != TIMER_PENDING) // sanity check
abort();
status_ = TIMER_HANDLING;
expire(e);
// if it wasn't rescheduled, it's done
if (status_ == TIMER_HANDLING)
status_ = TIMER_IDLE;
}</span>
這裏在執行handle函數的時候主要是關於狀態的改變以及expire函數。expire函數在TimeHandler中被聲明爲純虛函數,所以當你需要爲不同的實體所需要的定時器來進行不同的操作的時候你就可以override expire()函數來實現你的行爲。
在你自己所定義的Timer中的expire函數中,你就可以使用在TcpAgent中的TcpAgent實體來進行你想要的操作了,從而實現了用自己定義的Timer來操作特定的實體。