引言:最近在网上看了很多博客,想要深入了解大小端问题,主要是做毕设时,RTP包协议的结构体定义有两种方式,即大端和小端。但是一些博客并没有讲到理解大小端的本质问题,在这里按自己的理解扩充一下,可能有错,望理解!!!
1. 字节序
字节序即字节的存储顺序,如果数据都是单字节的,那怎么存储无所谓了,但是对于多字节数据,比如int,double等,就要考虑存储的顺序了。字节序是硬件层面的东西,通常只和你使用的处理器架构有关,而和编程语言无关。字节序分为大端序和小端序。
大端序:数据的高位字节存放在地址的低端 低位字节存放在地址高端。
小端序:数据的高位字节存放在地址的高端 低位字节存放在地址低端。
2. 比特序
字节序是一个对象中的多个字节之间的顺序问题,比特序就是一个字节中的8个比特位(bit)之间的顺序问题。一般情况下系统的比特序和字节序是保持一致的。以二进制数值10110101为例,其在不同平台下的内存位序如下:
大端的含义是数值的最高位1(最左边的1)放在了内存起始位置上,即数值10110101的大端内存布局为10110101。
小端的含义是数值的最低位1(最右边的1)放在了内存起始位置上,即数值10110101的小端内存布局为10101101。
3. 位域
对于位域有一个约定:在C语言的结构体中如果包含了位域,如果位域A定义在位域B之前,那么位域A总是出现在低地址的比特位。 这就决定了网络编程中位域在定义时必须处理大小端问题。(同样,结构体中前面的成员也处于较低的地址)
对于IP头,可以这样理解,网络传输时version在前,ihl在后,网络是大端序,可以认为version是数字的高位,ihl是低位,所以:
在大端机中,由于低地址是高位,所以位域version必须在前面。
在小端机中,由于高地址是高位,所以位域version必须在后面。
4. RTP头大小端处理
如下图这是网络大端序要接收数据的格式,RTP协议就是这样规定的,网络大端序必须按照这样的顺序发送数据,这样PC机客户端(支持RTP协议)接收到数据后,也会按照这样的格式解析。
(1)如果你的服务器是大端系统,所以代码中不需要转换,按照正常格式定义变量,在内存中也是按照大端模式存储。
struct _RTP_FIXED_HEADER
{
unsigned char version:2; /**//* expect 2 */
unsigned char padding:1; /**//* expect 0 */
unsigned char extension:1; /**//* expect 1, see RTP_OP below */
unsigned char csrc_len:4; /**//* expect 0 */
unsigned char marker:1; /**//* expect 1 */
unsigned char payload:7; /**//* RTP_PAYLOAD_RTSP */
unsigned short seq_no;
unsigned long timestamp;
unsigned long ssrc; /**//* stream number is used here. */
} __PACKED__;
(2)如果服务器是小端系统,由第二部分比特序可知,我们服务器的代码最后是在服务器上运行,所以定义的结构体变量会以小端方式存放在内存中,也就是我们需要将结构体的比特位倒序,然后存放在小端系统时,里面存放的值的顺序才是大端系统需要的,因为数据的发送与大小端无关,总是从低地址开始发送,直到数据总长度接收。所以我们在存储的时候就调整好位置,如下代码所示:
struct _RTP_FIXED_HEADER
{
/**//* byte 0 */
unsigned char csrc_len:4; /**//* expect 0 */
unsigned char extension:1; /**//* expect 1, see RTP_OP below */
unsigned char padding:1; /**//* expect 0 */
unsigned char version:2; /**//* expect 2 */
/**//* byte 1 */
unsigned char payload:7; /**//* RTP_PAYLOAD_RTSP */
unsigned char marker:1; /**//* expect 1 */
/**//* bytes 2, 3 */
unsigned short seq_no;
/**//* bytes 4-7 */
unsigned long timestamp;
/**//* bytes 8-11 */
unsigned long ssrc; /**//* stream number is used here. */
} __PACKED__;
(3)上述代码中只对比特序进行了调整,这些比特位的组合为一个字节,为单字节序,是不需要转换的,比如我们发送的视频流数据不需要转换,因为他是单字节流。而short与long是多字节序,所以在发送之前需要使用hton(主机转网络字节序)函数对多字节进行转换,代码如下:
ssrc = htonl(10);
seq_no = htons(g_rtspClients[is].seqnum++);
总结:内存中如果有一个存好的数,同样一个数,他在大端系统和小端系统的代码中的值却不一样,如果代码中有定义一个数,他在大端小端系统内存中存储的顺序却不一样。
5、多字节序
如下图:前6个变量为char,他们以字节为单位,各自位成员按大小端比较权重。而short为2字节,该变量本身以2字节为单位,内部以一字节按大小端比较权重,这才有了0x1234与0x3412。long为4字节,4字节为单位,一字节比较权重,这才有了0x12345678与0x78563412。
(1)如果在大小端系统中传递数据,所以有了转换大小端的说法,也出现了像htonl()、htons()这样的函数去转换大小端字节序的说法。host to network long/short,字节序的转换需要我们做。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)