网络编程 - 字节序

尽管计算机能识别的最小单位是bit,即0、1这样的二进制数。但是,出于方便编程和编码的考虑,人们仍将byte作为最小的处理单位。

1byte由8位bit组成:

1111 1111

绝大多数处理器在处理byte层级的数据时会以相同顺序组成bye值的bit数据。因此,单个的byte值在存放和传输操作时一般是相同的。但是,当一个数值超过1字节时,比如整数的4字节,在不同的处理器上就可能存在两种存放方式:大端序和小端序。这两种方式代表了处理器设计者对数据处理的两种思路,我们暂且只了解一下这两种方式的具体规则:

大端序(big-endian)

大端序是指高位数据存储在内存的低地址上,就高位数据排在前面。这跟我们在十进制中通常所说的高位在左相似。对于2个字节的短整型 0x3132 来说,当它存放在一个大端序的设备上时,其字节在内存上的排列应该是这样的:

地址增长  →
内存地址    | 0x1000 | 0x1001 
--------------------------- 
字节数据 | 0x31(高位字节) | 0x32 (低位字节)

0x31 是高位字节,在低地址0x1000上,0x32是低位字节在高地址的0x1001上。

小端序(little-endian)

小端序与大端序正好相反,0x32 低位字节放在了低地址0x1000上,0x31高位字节在高地址0x1001上。

地址方向  →
地址  | 0x1000 | 0x1001 
--------------------------- 
字节 | 0x32 (低位字节)| 0x31 (高位字节)

测试

$ vi endian_test.c

    #include <stdio.h>
    int main(){
        unsigned int val = 0x12345678;
        printf("[0]:0x%x\n",*((char*)&val + 0));
        printf("[1]:0x%x\n",*((char*)&val + 1));
        printf("[2]:0x%x\n",*((char*)&val + 2));
        printf("[3]:0x%x\n",*((char*)&val + 3));
    }

$ gcc endian_test.c -o endian_test
$ ./endian_test

通过测试上述代码,如果输出和下列一样则是小端设备,反之是大段:

[0]:0x78
[1]:0x56
[2]:0x34
[3]:0x12

网络字节序

从上面的介绍我们知道,一个数值在不同设备上的字节序可能不同,那么,当把这个值拿出来用作网络数据在不同的设备之间传输时该怎么办呢? 我们知道,网络传输的是byte数据,所以我们要一个指定一个字节序。大端序 or 小端序 ?

大概,进行了无数次扯皮之后。前人为我们统一了口径,“网络传输用大端序!“所以,大端序也被称之为网络字节序。

大小端序的判断

一,利用联合体来检测大小端。由于联合体 union 的存放顺序是所有成员都从低地址开始存放,利用该特性就可以轻松地获得了CPU对内存采用 little-endian 还是 big-endian 模式读写

/* return 1 小端  0 大端 */
int is_little_endian(){
    union {
        unsigned int a;
        unsigned char b;
    }c;
    c.a = 1;
    return (c.b == 1);
}

二,使用类型的强制转换实现

/* return 1 小端  0 大端 */
int is_little_endian(){
    unsigned short flag = 0x4321;
    if(*(unsigned char*)&flag == 0x21){
        return 1;
    }
    else{
        return 0;
    }
}

大小端转换函数

BSD Socket定义的一组转换函数,用于16和32bit整数在网络序和本机字节序之间的转换。

  • htonl,htons用于本机序转换到网络序
  • ntohl,ntohs用于网络序转换到本机序

参见


Comments