主机字节序和网络字节序
在使用网络进行程序设计中会碰到的一个问题是字节序的问题,这在基于单机或者同 类型机器进行开发的过程中很少遇到。由于网络的特点是将 Internet 上不同的网络设备和 主机进行连接和通信,这决定了使用网络进行开发的程序的特点就是要兼容各种类型的设备,其中的数据在不同的设备上要有唯一的含义。字节序的问题是上述情况下的典型问题。
1. 字节序的含义
字节序的问题是由于 CPU 对整数在内存中的存放方式造成的。多于一个字节的数据类型在内存中的存放顺序叫主机字节序。最常见的字节序有两种,小端字节序和大端字节序。
█ 小端字节序:即 Little Endian,简称 LE,将数据的最低字节放在内存的起始位置。小端字节序的特点是内存地址较低的位存放数据的低位,内存地址髙的位存放数据的高位,与思维习惯一致。采用低字节序的 CPU 有 x86 架构的 Intel 系列产品。
█ 大端字节序:即 Big Endian,简称 BE,将数据的高字节放在内存的起始位置。大端字节序的特点是内存中低字节位置存放数据的高位字节,内存中的高位字节存放数据的较低字节数据,与思维习惯不一致。但是与实际数据的表达方式是一致的。如果将内存中的数据直接存放在文件中,打开文件查看会发现和原来的数据的高低位一致。采用大端字节序的典型的代表有 PowerPC 的 UNIX 系统。
例如,对于一个 8 位字节的数据 0x12345678,假设在内存中存放的开始地址为 0x1000,则在小端字节序系统和大端字节序系统中的方式如下表(数据在小端字节序和大端字节序系统中的差别)所示。
内存地址 | 0x1000 | 0x1001 | 0x1002 | 0x1003 |
---|---|---|---|---|
小端字节序 | 0x78 | 0x56 | 0x34 | 0x12 |
大端字节序 | 0x12 | 0x34 | 0x56 | 0x78 |
而如果将 0x12345678 写入内存地址 0x1000 开始的地方,在内存中的值为下表(数据在小端字节序和大端字节序系统中的差别)所示的形式。
内存地址 | 小端字节序 | 大端字节序 |
---|---|---|
0x1000 | 0x12 | 0x78 |
0x1001 | 0x34 | 0x56 |
0x1002 | 0x56 | 0x34 |
0x1003 | 0x78 | 0x12 |
系统对多字节数据的不同存放方法造成了使用方法的问题,例如,在 x86 系统主机 A 上的一个值为 0x12345678,数据通过网络传送到 PowerPC上的一个运行 UNIX 的主机 B 上,在 B 上此值解释为 0x78563412,与原来的数据迥异,这样就造成了传输上兼容性方面的困难。
2. 网络字节序的转换
网络的字节序标准规定为大端字节序,不同平台上会对主机字节序进行转化后再进行传送,到主机后再转化为主机字节序,数据的传输就不会产生传输造成的问题了。同一个数据在不同的平台上可以使用网络字节序的转换函数来实现。
如下图(不进行网络字节序转换的传递数据)所示为主机 A 中的应用程序将变量 a 中的值 0x12345678,通过网络传递给主机 B 中的应用程序中的变量 b,如果不进行网络字节序转换,b 的值为 0x78563412。
如下图(通过网络字节序转换在网络间传递数据)所示,如果进行网络字节序转换,a 的值与 b 的值均为 0x12345678。
进行网络字节序转换的函数有 htons()、ntohs()、htonl()、ntohl() 等,其中 s 是 short 数据类型的意思,l 是 long 数据类型的意思,而 h 是 host,即主机的意思,n 是 network,即网络的意思。以上 4 个函数分别如下。
█ htons():表示对于 short 类型的变量,从主机字节序转换为网络字节序。
█ ntohs():表示对于 short 类型的变量,从网络字节序转换为主机字节序。
█ htonl():表示对于 long 类型的变量,从主机字节序转换为网络字节序。
█ ntohl():表示对于 long 类型的变量,从网络字节序转换为主机字节序。
字节序的转换函数并没有转换符号类型变量,是否为符号类型是由应用程式来确定的,与字节序无关。
字节序转换函数在不同平台上的实现是不同的,如对于 long 类型的转换,小端主机字节序的平台要进行转换,而在大端主机字节序的平台上是不需要进行转换的。例如下面的实现方式可以兼容不同的平台:
#if ISLE
/* 小端字节序平台调用此部分代码 */
long htonl(long value)
{
/* 进行转换,即位置 0x12345678 转换位置 → 0x78563412 */
return((value << 24)|((value << 8)&0x00FF0000)|((value >> 8)&0x0000FF00)|(value >> 24));
}
#else if ISBE
/* 大端字节序平台调用此部分代码 */
long htonl(long value)
{
/* 由于大端字节序平台与网络字节序一致,不需要进行转换 */
return value;
}
#endif
不同的平台的实现代码是不同的部分。其他函数的实现与此类似,注意 htons() 和 ntohs() 函数及 htonl() 和 ntohl() 函数是对应的转换,两个函数完全可以使用同一套代码,例如:
#define ntohl htonl
小结
ISO/OSI 网络模型是进行网络研究的基础,该模型对各层之间的功能进行了抽象,仔细地研究此模型会对网络协议栈的本质有更深入的了解。TCP 和 UDP 协议是应用程序设计中最常使用的两种协议。TCP 是可靠的协议,用很多机制来保证数据传输的可靠性。UDP 则是一种不可靠的协议,在实际使用过程中,发送端的数据如果在接收端没有及时地从协议栈缓冲区取出,有可能被之后到达的数据冲掉。
ICMP 是网络控制协议,用于发送网络的诊断、错误、控制信息。ARP 则是地址解析协议,获得 IP 地址对应的硬件地址;与 RARP 协议如 ARP 相反的协议,叫逆地址解析协议,获得硬件地址对应的 IP 地址,通常用于无盘工作站。
IP 地址是一种点分四段式的数据,表示一个主机在 Internet 上的网络位置,主要用于跨网主机的识别。掩码用于快速寻址和网络划分。