转自http://www.baisi.net/thread-60130-1-1.html
1. NS2中数据包packet类结构图:
packet类中的access函数如下:
inline unsigned char* access(int off) const {
if (off < 0)
abort();
return (&bits_[off]);
}
struct hdr_cmn(即共用common头)中的access函数如下:
inline static hdr_cmn* access(const Packet* p) {
return (hdr_cmn*) p->access(offset_);
}
从以上的函数可以看出只要给出特定的报头在packet中的偏移量offset_,立刻就能够取得指向该报头的指针(通过强类型转换,原理有点类似把一个子类强类型转换为其父类),从而访问或设置该报头内容。
常见TCL脚本中有如下语句:
remove-all-packet-headers
add-packet-header AODV ARP
...
set ns [new Simulator]
其中涉及到的函数如下:
proc add-packet-header args {
foreach cl $args {
PacketHeaderManager set tab_(PacketHeader/$cl) 1
}
}#add-packet-header函数把要激活的报头对应的tab_数组元素置1
脚本中new Simulator的工作之一就是创建包结构:
Simulator instproc create_packetformat { } {
PacketHeaderManager instvar tab_
set pm [new PacketHeaderManager]
foreach cl [PacketHeader info subclass] {
if [info exists tab_($cl)] {
set off [$pm allochdr $cl] #取得当前子报头在整个packet中的偏移
$cl offset $off #把这个赋给当前的子报头中的offset变量
}
}
$self set packetManager_ $pm
}
#下面的函数取得当前子报头在整个packet中的偏移
PacketHeaderManager instproc allochdr cl {
set size [$cl set hdrlen_] #取得子报头$cl的长度hdrlen_
$self instvar hdrlen_ #注意此处hdrlen_变量是PacketHeaderManager的
set NS_ALIGN 8
set incr [expr ($size + ($NS_ALIGN-1)) & ~($NS_ALIGN-1)]
#计算偏移,使用了向上进位保证了,incr的长度是一个字节(8 bit)的倍数。
set base $hdrlen_
incr hdrlen_ $incr # hdrlen_加上偏移
return $base #返回当前偏移
}
对于计算偏移NS手册上有如下表述:The procedure keeps a running variable hdrlen_ with the current length of BOB as new packet headers are enabled. It also arranges for 8-byte alignment for any newly-enabled packet header. This is needed to ensure that when double-world length quantities are used in packet headers on machines where double-word alignment is required, access faults are not produced.
综上取得包中子报头的基本思路:首先设置包结构(packet)的报头中含有多少个子报头,然后初始化包结构,把各个子报头的offsize字段设置成正确的偏移量。然后用access函数就可以很方便地访问各个子报头了。
2.移动节点的通信过程:
两个对等无线节点的结构图
两个移动节点之间的通信过程图
3.每个层对packet的设置:
总结:NS2中,packet结构在一次模拟中保持不变,packet在网络各个层次流动时,唯一的变化就是各个子报头中的内容改变了。即传输层把IP报头设置了,TCP协议把TCP报头设置了,网络层路由协议把相应的路由协议报头设置了,LL层根据IP地址把Mac包头的Mac地址等正确设置了;并没有加报头拆报头的过程。
注意: (以下是个人心得)
附录:实际中的数据包结构(以下部分资料来源网上)
1. IP报头格式:
1)版本字段标志:该分组采用的IP数据包结构的版本号, 对于常用的IPv4来说,该字段 的内容为4;对于IPv6来说,该字段的内容为6。
2)IHL字段:报头的长度, 以4Bit为单位,最小为5,最大值为l5, 即报头的最大长度为60Bit。
3)服务类型字段: 主机能告诉子网它需要的服务。
4)总长: 该数据包(包括报头和报文)的长度,最大是65 535Bit。
5)标志字段: 主机判断新来的分段属于哪个分组,所有属于同一个分组的分段应具有同样的标志值。
6)DF MF和分段偏移字段: 指示该分组是否需要分段, 是否全部到达, 该分段处于分组的位置。
7)生命期字段: 用来限制分组生命周期的计数器,最长生命周期为255 s。该字段将在每个节点中被递减处理, 当它被减到0时,该分组将被丢弃。
8)协议:该数据包采用的是哪一种TCP/IP应用协议,对于TCP协议,该字段为6;对于 UDP协议,该字段为ll。
9)头校验:校验头部,主要用于检测以路由器中的内存坏字带来的错误。
10)源地址和目的地址: 该数据包的来源和去向。
对于TCP/IP数据包而言, 仅仅掌握IP数据包的格式,还不能将数据正文从数据包中完全分离出来,还不能完全理解这个数据包的真正含义。在网络层之上的传输层,TCP/IP定义了2种数据传输协议:TCP和UDP,这2种协议也相应定义不同的数据结构。
2.TCP报头结构:
1)源端口和目的端口字段:标志出本地和远端的连接端口。
2)顺序号和确认号字段:TCP流特有的,TCP对每个数据字节进行编号。顺序号标志本分组的编号,确认号指示希望接收的下一个分组编号。
3)TCP头长表明在TCP头的长度,以4Bit为单位。这条信息对于定位确切的数据信息十分重要,因为选项字段的长度是不固定的,只有通过TCP头长的内容才能确认选项字段的长度。
4)URG、ACK、PSH RST、SYN、FIN等字段标志了该分组的控制功能。
5)窗口大小字段:指示的是TCP滑动窗控制内容。
6)校验和: 为了确保高可靠性而设置的,只校验该头部信息的数据。
3. UDP数据包的报头十分简单:
固定为8Bit,内容是分别为2Bit的源端口、目的端口、UDP长度和UDP校验和。其中UDP长度标志的是包括8Bit的报头及后面的报文在内的数据包长度;UDP校验和校验的是全部UDP数据包的内容。
4. 802.3 MAC帧的结构:
1)前导码:7个字节(10101010),用于收发双方的时钟同步。
2)帧起始定界符:1个字节(10101011),标志帧的开始。
3)目的地址和源地址:可为2或6个字节,但10Mbps的基带系统只用6字节地址。目的地址可分为:单地址:最高比特位为“0”,用于单播(unicast)。组地址:最高比特位为“1”,用于组播(multicast)。广播地址:全“1”,用于广播(broadcast)。通过次高位的不同来区分全局地址和局部地址:局部地址:次高位为“1”,由网络管理员指定,只用于本网中。全局地址:次高位为“0”,由IEEE统一分配,保证在世界上的唯一性。共有248-2≈7?1013个。
4)长度字段:2个字节(0~1500),用于指明数据域的字节数。
5)数据域:0~1500个字节,用于存放被传输的数据(LLC PDU)。
6)填充域:0~46个字节,802.3规定有效帧从目的地址到校验和字段的最短长度为64字节(固定部分为18字节),当数据域长度小于46个字节时,就使用本字段的填充来满足最短帧的要求。
7)校验和:4个字节,使用32位CRC校验,G(x)=x32+x26+x23+x22+x16+x12+x11+x10+x8+x5+x4+x2+x+1
5. 802.11MAC帧格式