微信搜索“编程笔记本”,获取更多信息
------------- codingbook2020 -------------
今天我们来学习一下在网络编程中一个很重要的问题:字节序。
在使用网络进行程序设计中会碰到的一个问题是字节序的问题,这在基于单机或者同类型机器进行开发的过程中很少遇到。由于网络的特点是将 Internet 上不同的网络设备和主机进行连接和通信,这决定了使用网络进行开发的程序的特点就是要兼容各种类型的设备,其中的数据在不同的设备上要有唯一的含义。字节序的问题是上述情况下的典型问题。
字节序的含义
字节序的问题是由于 CPU 对整数在内存中存放方式的不同而造成的。多于一个字节的数据类型在内存中的存放顺序叫主机字节序。最常见的字节序有两种,小端字节序和大端字节序。
- 小端字节序
即 Little Endian ,简称 LE ,将数据的最低字节放在内存的起始位置。小端字节序的特点是内存地址较低的位存放数据的低位,内存地址高的位存放数据的高位,与思维习惯一致。采用小端字节序的 CPU 有 X86 架构的 Intel 系列产品 - 大端字节序
即 Big Endian ,简称 BE ,将数据的高字节放在内存的起始位置。大端字节序的特点是内存中低字节位置存放数据的高位字节,内存中的高位字节存放数据的较低字节数据,与思维习惯不一致。但是与实际数据的表达方式是一致的。如果将内存中的数据直接存放在文件中,打开文件查看会发现和原来的数据的高低位一致。采用大端字节序的典型代表有 PowerPC 的 UNIX 系统。
例如,对于一个 8 位字节的数据 0x12345678 ,假设在内存中存放的起始地址为 0x1000,则在小端字节序系统和大端字节序系统中的方式如下图所示。
图片
图片
下面我们通过一个简单的例子来获取笔者所使用主机的字节序:
#include <stdio.h>
union Test {
short numb;
char c[2];
};
int main()
{
printf("sizeof(short) = %d\n", sizeof(short));
printf("sizeof(char) = %d\n", sizeof(char));
union Test t;
t.numb = 0x1234;
printf("numb = %x\n", t.numb);
printf("c[0] = %x\n", t.c[0]);
printf("c[1] = %x\n", t.c[1]);
return 0;
}
/*
运行结果:
sizeof(short) = 2
sizeof(char) = 1
numb = 1234
c[0] = 34
c[1] = 12
*/
从运行结果中,我们可以清晰地看出,低位地址存放的是低字节数据,高位地址存放的是高字节数据,所以笔者主机使用的是小端字节序(CPU: X86 Intel Core i5)。
系统对多字节数据的不同存放方式造成了使用上的问题。例如,在 X86 系统的主机 A 上的一个值为 0x12345678 ,数据通过网络传送到 PowerPC 上的一个运行 UNIX 的主机 B 上,在 B 上此值解释为 0x78563412 ,与原来的数据迥异,这就造成了传输上兼容性方面的困难。
网络字节序的转换
网络的字节序标准规定为大端字节序,不同平台上会对主机字节序进行转化后在进行传送,到主机后在转化为主机字节序,这样数据的传输就不会产生问题了。同一个数据在不同的平台上可以使用网络字节序的转换函数来实现。
图片
进行网络字节序转换的函数有 htons()
、ntohs()
、htonl()
、ntohl()
。其中,s 是 short 数据类型的意思,l 是 long 数据类型的意思,而 h 是主机(host)的意思,n 是网络(network)的意思。
- htons():表示对于 short 类型的变量,从主机字节序转换为网络字节序
- ntohs():表示对于 short 类型的变量,从网络字节序转换为主机字节序
- htonl():表示对于 long 类型的变量,从主机字节序转换为网络字节序
- ntohl():表示对于 long 类型的变量,从网络字节序转换为主机字节序
字节序的转换函数并没有转换符号类型变量,是否为符号类型是由应用程式来确定的,与字节序无关。字节序转换函数在不同平台上的实现是不同的,如对于 long 类型的转换,小端主机字节序的平台要进行转换,而在大端主机字节序的平台上是不需要进行转换的。
点击下方图片关注我,或微信搜索**“编程笔记本”**,获取更多信息。