首先是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来操作特定的实体。