通信过程(二)涉及到1字节内多个位段的网络传输过程

本文主要分析涉及到位段的协议如何正确地从某个计算机(不论大小端)通过网络传输到另一个计算机的过程。因为很多网络实现都是以linux为内核的(事实上是因为我只熟悉一点linux,其他的网络操作系统不熟悉而已),linux是用C编写的(事实上也是我只熟悉C的原因),本文以C语言作为编程语言来分析传输过程。

说到网络传输,相信没有不熟悉TCP/IP的吧?当然这里不是讲网络体系结构的,我们就以ip头协议规范为例来看看它在网络中的传输过程(其实这里和TCP/IP没什么大关系,这里就是傍个大概念而已,就像‘xx头衔’。)。为简单,我们只分析前2个字节:

IP 头的前2个字节(因为网络序是BE,协议本身是因网而生的,所以协议得到定义也是BE的):

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(1)

IP 头的前2个字节

试想在一个大端的系统下(还记得大端系统的2个重要概念吗?涉及到比特序和字节序),这段数据在内存中的排布是如何的?

排布如下(为了节省空间把4位的版本简化写成 ver或version, 4位首部长度简化写成ihl):

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(2)

知道了内存的排布,用C语言如何定义呢?发现结构体的定义和位段(可能大部分教科书可能叫位域,我习惯用位段)很适合这个:

一般C语言教科书中很少涉及到定义的结构体的内存排布,这里说明一下:

1、 C语言定义的结构体是从低内存开始排布的(不论大小端系统都是从低内存开始排布,注意这里暂时不考虑C语言结构体对齐的问题,事实上这里也不需要考虑)

2、 C语言定义的位段也是从低阶开始排列,也就是前文说的低地址开始分布的(这里也不论是否是大小端系统都是这样排列的)

3、 在定义的位段中,赋值的话是个大小端系统有关系的,(大端系统LSB在高阶(位段的高地址),小端系统相反)

4、 位序对于C语言是屏蔽的,也就是说在不同的端系统定义一个数,比如(0x12345678)做按位与、左移、右移操作,得到的值是一样的。比如(0x12345678&0xff 得到的值都是0x78)

这几点是C语言标准的规定,我理解的C语言是这样规定的。所以这个就很简单了,这个字段只需要这样定义就可以:

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(3)

这样我们的协议就定义出来了,下面我们再套用之前的图,看一看这个iphdr是如何从大端发送到小端的吧,假设 version 字段为 0x4(0100b), ihl为0x5(0101b), tos 为 0x12(00010010b), 这些值只是做传输实例,不一定有什么特殊意义:

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(4)

如果你仔细分析了上面这张图,就会发现:咦好像有点不对啊,我要发送的值是(ver = 0x4, ihr = 0x5),怎么到小端系统值变为了(ver = 0x5, ihr = 0x4)了呢?哪里出问题了吧?你之前说的那些概念都有自己扯的吧!但这里告诉你事实正是像传输的这样的,我们已经用那些概念解决了字节序的问题,同样这个问题也可以解决。那我们怎么解决呢?用C实现很容易喽,还记得前面的那对宏定义吗?__BIG_ENDIAN_BITFIELD 和 __LITTLE_ENDIAN_BITFIELD ,对,就是这一对。我们把它用上就可以了,像如下定义我们的结构体:

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(5)

事实上我们的linux内核也就是这样实现的。如果身边有内核源码,大家可以搜一下,内核中充斥着大量这样的应用。有了这个,不再需要额外的知识,我们的图也能自己完善了:

计算机网络字节填充规则(计算机网络传输字节序问题汇总)(6)

这就解决了所有问题了吗?到现在为止,所有的概念我能想到的都介绍完了。当然如果看到此处的各位大神如果有要补充的欢迎补充,不胜感激。

但在实际的使用中还有一些问题需要补充,那就是C语言对于超过1个字节的位段的定义和在网络通信中的使用。同样根据上面的概念我们也可以比较容易(事实上对我这个笨蛋而言可能要推导很久)地推导出来。在下一节中我们来看看这种情况如何用C语言定义的以及传输的过程。

,