本文主要介绍802.11协议中的退避算法的具体流程:
上图出自802.11-2007的文档,简单明了的解释了各个node在竞争信道时发生的故事。
下面两段代码对应802.11的send函数和resume函数:
send函数部分代码:
/*
* If the medium is IDLE, we must wait for a DIFS
* Space before transmitting.
*/
if(mhBackoff_.busy() == 0) {
if(is_idle()) {
if (mhDefer_.busy() == 0) {
/*
* If we are already deferring, there is no
* need to reset the Defer timer.
*/
if (bugFix_timer_) {
mhBackoff_.start(cw_, is_idle(),
phymib_.getDIFS());
}
else {
rTime = (Random::random() % cw_)
* (phymib_.getSlotTime());
mhDefer_.start(phymib_.getDIFS() +
rTime);
}
}
} else {
/*
* If the medium is NOT IDLE, then we start
* the backoff timer.
*/
mhBackoff_.start(cw_, is_idle());
}
}
}
当一个node准备send之前,首先要检查自己的backoff timer是否已经开始倒计时,如果已经开始倒计时,那么只要return等待timer的结束即可。因为任何情况下只要mhBackoff timer结束,此node就可以立即占用信道。这对应于node第一行中的判断。
tx_resume()部分代码:
if (mhBackoff_.busy() == 0) {//waiting for backoff timer times out at which time we can send data right now
if (bugFix_timer_) {
mhBackoff_.start(cw_, is_idle(),
phymib_.getDIFS());//
}
else {
rTime = (Random::random() % cw_) *
phymib_.getSlotTime();
mhDefer_.start( phymib_.getDIFS() + rTime);
}
}
首先,对于node B 来说,他首先check自己的mhBackoff timer,发现not busy;接着检查信道是否空闲(is_idle()),此时因为上图中信道被A占据,信道忙,所以B被迫进入defer阶段,等待信道的再次空闲。此时,对应的代码段因为is_idle()判断为否,直接进入else 语句中,设定自己的Backoff timer。这里为什么本来要进入defer timer却要设置Backoff Timer呢?主要是由于Backoff Timer的class确定的。
解释:1.在backoff timer被设置为cw_的瞬间,由于第二个参数is_idle()为0,start()函数会立即将paused_设为1,即立即暂停backoff timer,所以此时实际上并不会开始倒计时,而是要等到resume()函数被唤醒之后才会继续。
2.start(cw_, is_idle())这句有两个疑点。第一,backoff的时间应该是random%cw_*slottime而不是固定的cw_; 第二,在被唤醒之后,除了等待random的backoff 时间,802.11中规定了首先要冻结DIFS之后才可以倒计时backoff timer. 事实上,这两点分别在mac-timers.cc中的BackoffTimer::start(int cw, int idle, double difs) 和BackoffTimer::resume(double difs)中给出了解释。即backoff的时间是在start()中设置的,而resume之后的DIFS是在resume()中加入的。
对于node C和D也是采取同样的措施。
那么,当信道为空闲时,B、C、D的backoff timer同时被唤醒,执行resume()函数,首先等待DIFS,接着开始timer的countdown,由于C比较幸运,backoff timer率先结束,于是直接占用信道。此时,信道状态变为忙,于是B和D的backoff timer也被迫暂停(图中backoff的灰色部分表示剩余的冻结时间),直到C的数据传输完毕。重复以上过程,接下来D、E和B分别占用信道,整个过程结束。
对于NS2 send()函数中剩余部分的解释如下:
拿下图来说,我们假定此时node B还没有backoff,即忽略下图中Frame前面的那一小段backoff;并且假定node B将要开始传送数据时信道为空闲状态。
那么,对应上述代码,首先check mhBackoff为闲,接着check is_idle()为真,进入mhDefer的判断。如果defer timer已经被前面的将要send出去的数据包设置过了,那么就无需再设,只需要等待他的time out即可。否则的话,开始设置占用信道的backoff timer.这时候引入了一个bugFix_timer_的标志,具体参见点击打开链接 此文。
在NS2的default.tcl的设置中,bugFix_timer_是设为真的,所以这时候我们就直接进入mhBackoff_.start(),开始倒计时。但是注意,这时候start()函数里加了一个phymib_.getDIFS(),即加入了一个difs,这与上文所述并不矛盾。因为,上文中所述,信道正处于忙碌状态,backoff 会立即暂停并在resume后由resume()函数加入difs;但此时由于信道为空闲,backoff timer会立即启动,那么自然要手动加入这个difs了。