不同CPU中,4字节整数1在内存空间的存储方式是不同的。4字节整数1可用2进制表示如下:
00000000 00000000 00000000 00000001
有些CPU以上面的顺序存储到内存,另外一些CPU则以倒序存储,如下所示:
00000001 00000000 00000000 00000000
若不考虑这些就收发数据会发生问题,因为保存顺序的不同意味着对接收数据的解析顺序也不同。
大端序和小端序
CPU向内存保存数据的方式有两种:
- 大端序(Big Endian):高位字节存放到低位地址(高位字节在前)。
- 小端序(Little Endian):高位字节存放到高位地址(低位字节在前)。
仅凭描述很难解释清楚,不妨来看一个实例。假设在 0x20 号开始的地址中保存4字节 int 型数据 0x12345678,对于大端序,最高位字节 0x12 存放到低位地址,最低位字节 0x78 存放到高位地址。
大端序CPU保存方式如下图所示:
图1:整数 0x12345678 的大端序字节表示
小端序的保存方式如下图所示:
图2:整数 0x12345678 的小端序字节表示
网络字节序和主机字节序
网络字节序是确定的,而主机字节序是多样的。
网络字节序统一为大端序。
主机字节序既可以是大端的,也可以是小端的,现代计算机大多采用小端字节序。
不同CPU保存和解析数据的方式不同(主流的Intel系列CPU为小端序),小端序系统和大端序系统通信时会发生数据解析错误。
为了避免这个问题,约定数据在不同计算机之间传递时都采用大端字节序,也叫作网络字节序。通信时,发送方需要把数据转换成网络字节序(大端字节序)之后再发送,接收方再把网络字节序转成自己的字节序。主机A先把数据转换成大端序再进行网络传输,主机B收到数据后先转换为自己的格式再解析。
网络字节序转换函数
htons() 用来将当前主机字节序转换为网络字节序,其中h
代表主机(host)字节序,n
代表网络(network)字节序,s
代表short,htons 是 h、to、n、s 的组合,可以理解为”将short型数据从当前主机字节序转换为网络字节序“。
常见的网络字节转换函数有:
- htons():host to network short,将short类型数据从主机字节序转换为网络字节序。
- ntohs():network to host short,将short类型数据从网络字节序转换为主机字节序。
- htonl():host to network long,将long类型数据从主机字节序转换为网络字节序。
- ntohl():network to host long,将long类型数据从网络字节序转换为主机字节序。
通常,以s
为后缀的函数中,s
代表2个字节short,因此用于端口号转换;以l
为后缀的函数中,l
代表4个字节的long,因此用于IP地址转换。
例(在这个例子中使用了htons函数):
//创建sockaddr_in结构体变量 struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); //每个字节都用0填充 serv_addr.sin_family = AF_INET; //使用IPv4地址 serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具体的IP地址 serv_addr.sin_port = htons(1234); //端口号