如何传送一个封包(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:ffset_);
/* 检查要传送的封包大小是否是小于RTSThreshold或是不是一个broadcast的封包,若是的话,就不需要传送RTS.若是在使用者所写的 tcl中没有指定RTSThreshold,则ns2会去读取ns-default.tcl的值,内定为0,因此若是使用unicast,则一定会送出去 RTS */
if( (u_int32_t) HDR_CMN(pktTx_)->size() < macmib_.getRTSThreshold() || (u_int32_t) dst == MAC_BROADCAST) {
Packet::free(p);
return;
}
ch->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() < macmib_.getRTSThreshold() || (u_int32_t) ETHER_ADDR(mh->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中802.11代码深入理解—packet传输的流程 (转帖)相关推荐

  1. Redis中使用Java代码的方式实现发布订阅流程

    场景 Redis中的发布与订阅的概念与以命令行的方式实现发布订阅举例: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/105042 ...

  2. java中关于length的真确理解~~~~有补充的请跟帖~~~

    java中的length到底是个什么东西呢?有人说它是一个类,一个方法,如果是方法,它没有括号, 我只知道它可以得出数组的长度,但是今天遇到一段代码,怎么都解释不通,那就是绝对不会把循环的次数与数组的 ...

  3. 802.11 参考手册

    目录 一.802.11成帧封装实现    3 1.1控制帧    3 1.1.1 一般的帧控制位    3 1.1.2   RTS(请求发送)    4 1.1.3   CTS (允许发送)    5 ...

  4. 802.11的物理层-CS/CCA过程

    参考文献 [1] 802.11 Wireless Networks: The Definitive Guide, Mattbew S. Gast [2] Overhaul of IEEE 802.11 ...

  5. 关于 802.11 协议

    目录 一.802.11成帧封装实现    3 1.1控制帧    3 1.1.1 一般的帧控制位    3 1.1.2   RTS(请求发送)    4 1.1.3   CTS (允许发送)    5 ...

  6. NS2中数据包Packet的分析 (转帖)

    1. NS2中数据包packet类结构图: packet类中的access函数如下: inline unsigned char* access(int off) const {      if (of ...

  7. 理解NS2中的OTcl/tclCL

    题记 真正触动我写这篇短文的原因是试图理解NS2的基本原理. 在"the NS2 manual"中, 解释了为什么采用了两种语言来建构整个系统, 然后在第三章描述了tclcl的六个 ...

  8. 802.11协议中帧控制域中To DS and From DS 比特位的含义

    1. Does the Stations have knowledge about the Distribution  System the AP is connected to ? 2. are t ...

  9. 有关WLAN与wifi、WPAN与Bluetooth、WPAN与802.15、wifi与802.11、WiMAX与802.16等关系的一点理解

    http://blog.csdn.net/jbb0523/article/details/7269964 题目:有关WLAN与wifi.WPAN与Bluetooth.WPAN与802.15.wifi与 ...

最新文章

  1. 【怎样写代码】参数化类型 -- 泛型(一):泛型概述
  2. c语言中输入位权1,精通C语言程序设计1-编程基础.pptx
  3. 风之语.人在职场也需要'备胎'
  4. mysql的一些初步使用!mysqlcheck mysqladmin 建立删除修改表,库,等
  5. java 8 stream_深度分析:java8的新特性lambda和stream流,看完你学会了吗?
  6. mongodb 物理删除数据
  7. C语言隐式/显式类型转换 | C++四种强制类型转换、类的隐式转换、explicit
  8. python如何获取javascript动态产生的数据
  9. 颜维伦暗示Sun将与英特尔合作研发芯片
  10. TFS 2017 持续集成速记
  11. Xamarin入门一 环境准备
  12. 数据库自学-SQL-四大SQL语句DDL、DML、DQL、DCL
  13. 显著性检验--学习笔记
  14. 企业邮件服务器哪个好?常用邮箱客户端是哪个?
  15. Caused by: java.lang.Error: Unresolved compilation problems:解决办法
  16. 自制网页(仿B站)前端开源程序
  17. 【颜纠日记】分享5个理财规划步骤,5招更早过上理想生活
  18. 电商不打烊、全家出境游、美颜全家福,新一轮春节方式你中标几个?
  19. TP-LINK路由器如何设置上网
  20. (译)2019年前端性能优化清单 — 中篇

热门文章

  1. 主键需要键索引吗_MySQL 索引的知识点都在这里了,建议收藏!
  2. android时间utc,Android获取UTC时间的方式
  3. java判断波动的曲线是否大体平衡_基于标的物价格和波动率的期权投资策略
  4. 学会使用 diff 和 patch 命令协同开发
  5. MySQL 中的共享表空间与独立表空间如何选择
  6. 在Centos8 中使用Stratis管理本地存储(一)
  7. 模拟真机环境_QFramework 使用指南 2020(八):Res Kit(2)模拟模式与非模拟模式...
  8. 【Java】面试高频考题---topK问题详解(堆heap求解)
  9. python 线性回归_用Python实现线性回归算法
  10. linux和windows双系统引导,windows和linux双系统引导问题