如何传送一个封包(How to transmit a packet?)
首先,我们要看的第一个function是在mac-802_11.cc内的recv( ),程式会先判断目前呼叫recv( )这个packet的传输方向,若是DOWN,则表示此packet是要送出去的,因此就会再呼叫send(p, h).所以接着,我们跳到send( ),此send( )首先会去检查energy model,若是目前这个node是在睡眠状态(sleep mode),则把此packet给丢弃.然后会把handler h设定给callback_.下一步,就是去呼叫sendDATA(p)和sendRTS(ETHER_ADDR(dh->dh_ra)).
底下是sendDATA的程式码. (部份英文说明和程式码,会因为长度的关系而拿掉,所以读者最好还是拿原本的程式码做对照)
void Mac802_11::sendDATA(Packet *p)
{
hdr_cmn* ch = HDR_CMN(p);
struct hdr_mac802_11* dh = HDR_MAC802_11(p);
/* 更新packet的长度,把packet的长度加上PreambleLength (内定值为144 bits), PLCPHeaderLength(内定值为48bits), Mac Header Length和ETHER_FCS_LEN */
ch->size() += phymib_.getHdrLen11();
/* 填入Mac Header中frame control内的子栏位值 */
dh->dh_fc.fc_protocol_version = MAC_ProtocolVersion;
dh->dh_fc.fc_type = MAC_Type_Data;
dh->dh_fc.fc_subtype = MAC_Subtype_Data;
//printf("…..p = %x, mac-subtype-%d\n",p,dh->dh_fc.fc_subtype);
dh->dh_fc.fc_to_ds = 0;
dh->dh_fc.fc_from_ds = 0;
dh->dh_fc.fc_more_frag = 0;
dh->dh_fc.fc_retry = 0;
dh->dh_fc.fc_pwr_mgt = 0;
dh->dh_fc.fc_more_data = 0;
dh->dh_fc.fc_wep = 0;
dh->dh_fc.fc_order = 0;
/* 记录传送所需要花的时间,计算的方式(PreambleLength +PLCPHeaderLength) * 8 / PLCPDataRate + 剩于的封包长度(单位为bytes) * 8 / dataRate_ */
/* 事实上,底下的这一行程式码是个浪费,因为底下又会针对是否为broadcast或unicast的封包,再计算一次 */
ch->txtime() = txtime(ch->size(), dataRate_);
/* 若是这是一个unicast的封包 */
if((u_int32_t)ETHER_ADDR(dh->dh_ra) != MAC_BROADCAST) {
/* 再一次计算传送所需要花的时间 */
ch->txtime() = txtime(ch->size(), dataRate_);
/* duration的意思是送出去此data packet之后,此次的通讯还需要占用channel所需要的时间,这个时间的长度为传送一个ACK和一个SIF的时间 */
dh->dh_duration = usec(txtime(phymib_.getACKlen(), basicRate_)+ phymib_.getSIFS());
} else {
/* 若这是一个multicast的封包 */
ch->txtime() = txtime(ch->size(), basicRate_);
/* 若是multicast packet,送出去此data packet之后,就算传送完成,不需要再等待ACK,因此duration为0 */
dh->dh_duration = 0;
}
/*当Mac Header中的资讯都填完后,我们先把此packet暂时地存放在Mac Layer中的local buffer,等待适当的时机再传送出去 */
pktTx_ = p;
}
底下是sendRTS的程式码. (部份英文说明和程式码,会因为长度的关系而拿掉,所以读者最好还是拿原本的程式码做对照)
void Mac802_11::sendRTS(int dst)
{
Packet *p = Packet::alloc();
hdr_cmn* ch = HDR_CMN(p);
struct rts_frame *rf = (struct rts_frame*)p->access(hdr_mac::offset_);
/* 检查要传送的封包大小是否是小于RTSThreshold或是不是一个broadcast的封包,若是的话,就不需要传送RTS.若是在使用者所写的 tcl中没有指定RTSThreshold,则ns2会去读取ns-default.tcl的值,内定为0,因此若是使用unicast,则一定会送出去 RTS */
if( (u_int32_t) HDR_CMN(pktTx_)->size() uid() = 0;
ch->ptype() = PT_MAC;
ch->size() = phymib_.getRTSlen();
ch->iface() = -2;
ch->error() = 0;
bzero(rf, MAC_HDR_LEN);
/* 设定RTS packet中Mac Header的栏位 */
rf->rf_fc.fc_protocol_version = MAC_ProtocolVersion;
rf->rf_fc.fc_type = MAC_Type_Control;
rf->rf_fc.fc_subtype = MAC_Subtype_RTS;
rf->rf_fc.fc_to_ds = 0;
rf->rf_fc.fc_from_ds = 0;
rf->rf_fc.fc_more_frag = 0;
rf->rf_fc.fc_retry = 0;
rf->rf_fc.fc_pwr_mgt = 0;
rf->rf_fc.fc_more_data = 0;
rf->rf_fc.fc_wep = 0;
rf->rf_fc.fc_order = 0;
/* 把要传送的目的位址存放到RA */
STORE4BYTE(&dst, (rf->rf_ra));
/* 存放传送RTS所需要花的时间, RTS Frame是用basicRate_传送 */
ch->txtime() = txtime(ch->size(), basicRate_);
/* 把传送端的位址放到TA */
STORE4BYTE(&index_, (rf->rf_ta));
/* 计算duration,计算的公式为: SIF + T(CTS) + SIF + T(Pkt) + SIF + T(ACK) */
rf->rf_duration = usec(phymib_.getSIFS()
+ txtime(phymib_.getCTSlen(), basicRate_)
+ phymib_.getSIFS()
+ txtime(pktTx_)
+ phymib_.getSIFS()
+ txtime(phymib_.getACKlen(), basicRate_));
/* 把建立好的RTS packet先暂时存放到pktRTS_ */
pktRTS_ = p;
}
看完sendDATA( )和sendRTS( )之后,我们再回到send( ).接着,就指定一个unique sequence number给这个data packet.为了更清处的说明,底下把剩余的程式码贴在底下.
/ * 这是在send( )内的程式码 */
/* 若是目前backoff timer并没有在 count down */
if(mhBackoff_.busy() == 0) {
/* 此时channel又是idle */
if(is_idle()) {
/* 若是节点已经再等待defer timer,则让defer timer继续,因此不做任何的设定.但是若没有defer timer,就要根据802.11的规定,需要再等待一个DIFS和一个random time才能做资料的传送,而这个random time是由[0, cw_]所决定的 */
if (mhDefer_.busy() == 0) {
rTime = (Random::random() % cw_)*(phymib_.getSlotTime());
mhDefer_.start(phymib_.getDIFS() + rTime);
}
/* 此时channel若是busy */
else {
mhBackoff_.start(cw_, is_idle());
}
}
做完以上的事情后, send()已经完成了.
然后,当Defer timer expires的时候,程式就会去呼叫deferHandler(),在deferHandler()中会先去呼叫check_pktCTRL(),但因 为目前pktCTRL没有资料(回传-1),所以会继续去执行check_pktRTS().若是目前channel是idle的状 态,check_pktRTS()内的程
式码就会去设定传输状态为MAC_RTC,并且计算送出RTS timeout的时间,算法为:
timeout = txtime(phymib_.getRTSlen(), basicRate_)
+ DSSS_MaxPropagationDelay // 设定为2 us,可以参考mac-802_11.h
+ phymib_.getSIFS()
+ txtime(phymib_.getCTSlen(), basicRate_)
+ DSSS_MaxPropagationDelay; // 设定为2 us,可以参考mac-802_11.h
设定完后,就会去执行transmit(pktRTS_, timeout),把RTS的packet送出去.
送完RTS后,我们必需等待CTS,所以我们再回到recv()中的mhRecv_.start(txtime(p)),这个程式码主要是等待整个 packet完全接收后就会去呼叫recvHandler(),而recvHandler()就会再去呼叫recv_timer(),若是判断所收到的 packet是CTS,则再呼叫recvCTS(pktRx_).在recvCTS()中,因为已收到CTS,则代表RTS已传送成功,因此把 pktRTS_ = 0和ssrc_=0,然后再呼叫tx_resume().
在tx_resume()中,由于已成功的做完RTS/CTS,现在要准备送出data.这部份的程式如下所示
/ * 若是pktTx_有资料要传送 */
else if(pktTx_) {
if (mhBackoff_.busy() == 0) {
hdr_cmn *ch = HDR_CMN(pktTx_);
struct hdr_mac802_11 *mh = HDR_MAC802_11(pktTx_);
/* 判断packet size是否小于RTSThreshold或者是不是broadcast */
if ((u_int32_t) ch->size() dh_ra) == MAC_BROADCAST) {
rTime = (Random::random() % cw_) * phymib_.getSlotTime();
mhDefer_.start(phymib_.getDIFS() + rTime);
} else {
/* 若是unicast且packet size大于RTSThreshold,则会等待一个SIFS后,再把data packet送出去 */
mhDefer_.start(phymib_.getSIFS());
}
}
}
等到defer timer expires后,又会呼叫deferHandler(),而在deferHandler()又会再去呼叫check_pktTx(). check_pktTx()的程式码如下:
int Mac802_11::check_pktTx()
{
struct hdr_mac802_11 *mh;
double timeout;
assert(mhBackoff_.busy() == 0);
if(pktTx_ == 0)
return -1;
mh = HDR_MAC802_11(pktTx_);
switch(mh->dh_fc.fc_subtype) {
case MAC_Subtype_Data:
/* 若是目前的channel是busy的话,就需要增加contention window,然后再执行一次backoff */
if(! is_idle()) {
sendRTS(ETHER_ADDR(mh->dh_ra));
inc_cw();
mhBackoff_.start(cw_, is_idle());
return 0;
}
/* 设定传输状态为MAC_SEND */
setTxState(MAC_SEND);
if((u_int32_t)ETHER_ADDR(mh->dh_ra) != MAC_BROADCAST)
timeout = txtime(pktTx_)
+ DSSS_MaxPropagationDelay // 设定为2 us,可以参考mac-802_11.h
+ phymib_.getSIFS()
+ txtime(phymib_.getACKlen(), basicRate_)
+ DSSS_MaxPropagationDelay; // 设定为2 us,可以参考mac-802_11.h
else
timeout = txtime(pktTx_);
break;
default:
fprintf(stderr, "check_pktTx:Invalid MAC Control subtype\n");
exit(1);
}
/* 把pktTx_内的data packet传送出去 */
transmit(pktTx_, timeout);
return 0;
}
资料送出去后,若是unicast的data packet就需要等待ACK,所以我们再回到recv()中的mhRecv_.start(txtime(p)),这个程式码主要是等待整个 packet完全接收后就会去呼叫recvHandler(),而recvHandler()就会再去呼叫recv_timer(),若是判断所收到的 packet是ACK,则会呼叫recvACK(pktRx_). 由于已成功收到ACK,则表示DATA packet已成功的送出,所以就把mhSend_.stop(),判断packet size是否有大于RTSThreshold,若是有大于的话,就把slrc_ = 0,没有的话,就把ssrc_=0.并且把pktTx_=0. 最后在结束之前,再呼叫tx_resume().而tx_resume()会呼叫callback_….然后整个DATA packet传送过程就结束
【NS2】NS2中802.11代码深入理解—packet传输的流程(转载)
时间: 2024-12-19 19:07:34
【NS2】NS2中802.11代码深入理解—packet传输的流程(转载)的相关文章
关于阮一峰老师es6(第三版)中管道机制代码的理解浅析
最近正在学习阮一峰老师的es6(第三版)教材,在学到第七章<函数的扩展>中的箭头函数嵌套时,文中提到了一个关于“管道机制”的示例,文中源代码如下: //es6(第三版)教材中的管道机制源代码: const pipeline = (...funcs) => val => funcs.reduce((a, b) => b(a), val); const plus1 = a => a + 1; const mult2 = a => a * 2; const addThe
在ns2.35中添加myevalvid框架
在用ns2进行网络视频通信仿真的时候,先要为我们自己的ns2添加evalvid或者myevalvid框架.其中myevalvid框架是由柯志亨老师整合evalvid和ns2之后得出的新框架,笔者建议大家安装该框架,而不要安装原生的evalvid框架.这样就可以结合柯志亨老师的<ns2仿真实验-----多媒体和无线网络通信>这本书,做配套的实验,比较方便. 网上关于myevalvid框架的安装文章很多,大家可以参考去做,笔者会在本文最后给出相关的链接.本文主要是对myevalvid框架源码中的错
从linux0.11中起动部分代码看汇编调用c语言函数
上一篇分析了c语言的函数调用栈情况,知道了c语言的函数调用机制后,我们来看一下,linux0.11中起动部分的代码是如何从汇编跳入c语言函数的.在LINUX 0.11中的head.s文件中会看到如下一段代码(linux0.11的启动分析部分会在另一部分中再分析,由于此文仅涉及c与汇编代码的问题,). after_page_tables: pushl $0 # These are the parameters to main :-) pushl $0 pushl $0 pushl $L6 # re
.Net中BS端代码 理解
按钮 触发功能 <a class="button whiteButton" href="javascript:void()" onclick="AdminInfoPwd_EditPwd()">确定</a></span> function AdminInfoPwd_EditPwd() { $(".errorMsg").html(""); var pwd
CodeFirst中导航属性的代码实现 理解
导航属性是在CodeFirst中,两中数据库表之间,多对多或者1对多中表关联的属性.导航属性并不带有数据, 包括以下信息: 名称. (必需) 导航属性要导航的关联. (必需) 导航属性要导航的关联端. (必需) 对于多对多的导航属性,两张表是可选的.如果对关联一端的某实体类型定义导航属性,则不需要对关联另一端的该实体类型定义导航属性. 导航属性的数据类型是由其远程关联端的重数决定的. 重数:在关联的一端可以存在的实体类型实例的数量.关联端重数可以有以下列值之一: 一(1):表明在关联端存在且
802.11协议精读12:初探协议性能
序言 在初始的802.11协议版本之后,陆续更新的802.11e,以及802.11n以及更新的802.11技术,其都是基于改善当前802.11协议的缺陷不断进行改进的.为了理解这些改进,我们首先要理解802.11存在的一些问题,其中一个主要的问题就是性能问题. 本文我们先简单介绍802.11中一个常见问题,即路由器的宣称速率不等于实际速率的问题,然后我们具体分析一下这个宣称速率(即物理层速率)的计算方法.在后面一篇文章中,我们会介绍用数学方法对该吞吐量具体进行估计的方法,即Bianchi模型.
802.11协议精读10:节能模式(PSM)
序言 在802.11主要的版本中,总共定义了四种节能模式,本文主要关注最初始的PSM模式,对于在802.11e中添加的ASPD以及802.11n中添加的PSMP,SMPS机制,我们在下一篇再进行论述. PSM(Power Save Mode):802.11协议中初始的节能模式,其对基础架构模式和IBSS模式下的节能机制分别进行了定义,并且在DCF和PCF模式下,其具体的MAC层工作机制也有不同. 如同我们之前的描述,802.11的节能模式基本思想是:AP缓存下行数据,只有当节点休眠结束后主动向A
4.802.11协议笔记:PCF工作模式
序 在前面我们叙述过,在802.11的MAC层中,分成了两种基本工作模式: DCF(Distributed Coordination Function) PCF(Point Coordination Function) 我们已经介绍过了DCF的工作模式,而PCF模式当前只有协议中进行了规范,而在实际产品中几乎很少见到.所以就像在802.11权威指南和CWNA书本中所述一样,对于PCF的机制仅仅是协议规定,而几乎没有产品,所以纯粹对协议希望有深入了解的话,那么才需要对此进行阅读.这里仅仅是将笔者读
802.11 对于multicast 和 broadcast的处理
ethernet内部会有broadcast 和 multicast.这两种包都是一个STA向多个STA发包. 当没有wifi存在的时候,LAN口之间的broadcast 和 multicast是可靠转发的,但是若有wifi存在就不一样了. 电源考量 根据协议,broadcast和multicast在DTIM的时候AP会发送给STA.DTIM在AP中的设置一般是一倍的TIM. 当DTIM增加的时候,会更加省电,因为出于PS模式下的STA醒来的次数变少了.但是这也会导致某些应用的延时加大. DTIM