网络序和主机序、大端和小端问题详解
目录
5. 如果场景是host1发送tcp报文报文至host2,但是host2接收时并未进行网络序转主机序,那么将会出现什么情况?
1. 什么是网络序、主机序、大端、小端
大端、小端都指的是一种存储方式,网络字节序使用的是大端方式,大部分计算机使用的是小端模式。
-
大端
高位存放在低地址,低位存放在高地址。数据字节位随着内存地址的增长而减小。
大端即网络抓包时看起来舒服的那种字节序,符合我们的阅读习惯
-
小端
高位存放在高地址,低位存放在低地址。数据字节位随着内存地址的增长而增长。
小端存储阅读时是反人类的,但是有利于计算机硬件低位计算的方式
2. 为什么要区分大端与小端
-
cpu硬件设计中,为了方便计算一般都是从低位计算的,小端存储方式有利于低位计算。
-
但是小端方式不利于我们阅读,网络上进行抓包时使用大端方式解决了这个问题。
3. 什么时候需要转换大端与小端
-
内存存储以Byte为单位,当涉及一个Byte的变量存储时是没有区分的
-
c语言中内置的数据类型有int、double、short等多字节类型,存储该类型时将有两种方式,即上述的大端(低地址高位)与小端(低地址低位),这类的变量在由host1发送至host2时,需要进行大小端转换,即host1发往网络进行hton,host2接收时进行网络序转主机序。
4. 实际验证
-
主机序、小端验证
使用UINT16短整型变量存储以太帧类型,使用0x0800为例,利用gdb查看实际内存存储
(gdb) l
1 #include<stdio.h>
2
3 int main()
4 {
5 unsigned short eth_type=0x0800;
6
7 return 1;
8 }(gdb) b main
Breakpoint 1 at 0x400478: file gdb_test.c, line 5.
(gdb) r
Starting program: /home/fdu/Desktop/gdb_testBreakpoint 1, main () at gdb_test.c:5
5 unsigned short eth_type=0x0800;
Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.212.el6.x86_64
(gdb) n
7 return 1;
(gdb) p/x eth_type
$1 = 0x800
(gdb) x/2xb ð_type
0x7fffffffe50e: 0x00 0x08
可以发现0x0800实际在计算机中存储时使用小端方式,(0x0008),但是在代码中我们并不用感知该存储方式。
-
网络序、大端验证
通过抓包可以看到网络字节流中,0x0800存储为大端方式(0x0800)。
5. 如果场景是host1发送tcp报文报文至host2,但是host2接收时并未进行网络序转主机序,那么将会出现什么情况?
报文字节流是网络字节序,host2接收后并未进行主机序转换(仍以大端存储),此时如果进行报文解析(以小端方式解析),解析出来的报文将会出错,以以太帧类型为例
uint8 *raw_buf = m_buf; //指向报文字节流,未进行大小端转换
uint16 eth_type = (uint16 *)(raw_buf + 12); //eth_type存储报文类型,未进行大小端转换
以太帧类型网络字节序为大端0x0800
当原封不动到达主机端,eth_type由强转获取,即内存存储的仍为0x08 0x00
(gdb) x/2xb ð_type
0x7fffffffe50e: 0x08 0x00
但是,需要注意的是此时host2将以小端方式解析该变量,即内存0x7fffffffe50e: 0x08 0x00将解析为0x0008
(gdb) p/x eth_type
$3 = 0x8
6. 总结
-
大端和小端只是两种存储方式,但是无论如何存储,只要按照各自的解析方式,解析出来的含义是一致的就OK。例如host1为小端存储,host2为大端存储,但是两个计算机中运行的c程序中变量eth_type永远都是0x0800这种我们熟知含义的协议号。所以程序员不用过分关注大小端。
-
不进行大小端转换,将会导致原来大端模式存储的数据,却以小端存储方式来解读,必然会导致错误。