全部博文(83)
分类: LINUX
2008-04-11 11:05:43
在说明lwip中如何进行主机序和网络序的转换时,我们有必要先了解几个基本概念:MSB、LSB、Big Endian、Little Endian、网络序和主机序。
MSB是Most Significant Bit/Byte的首字母缩写,通常译为最重要的位或者最重要的字节;那么对于一个数字而言,什么是MSB呢?显然最高位是MSB,例如15430,1就是MSB,因为它在万位,它的变化是以10000为基数的。知道了MSB,LSB也就不难理解;LSB是Least Significant Bit/Byte的首字母缩写,通常译为最不重要的位或者最不重要的字节;对于15430而言,0显然是LSB,因为它在各位,它的变化对于整个数值的大小影响最小。
Big Endian和Little Endian是描述排列存储在计算机内存里的字节序列的术语。之所以出现两种排列次序,是由于CPU的两大对立阵营的对抗导致的,PowerPC(Moto&IBM) VS X86 = Big Vs. Little。在Big Endian机制中最重要字节(MSB)存放在最低端的地址上;而Little Endian机制中,最不重要字节(LSB)存放在最低端的地址上。
例如0x12345678在采用Big Endian的CPU(PowerPC为代表)中,其存放顺序为:
0x0000 |
12 |
0x0001 |
34 |
0x0002 |
56 |
0x0003 |
78 |
而在采用Little Endian的CPU(X86为代表)中,其存放顺序为:
0x0000 |
78 |
0x0001 |
56 |
0x0002 |
34 |
0x0003 |
12 |
关于Big Endian和Little Endian还有一点需要说明的是:软件只需要关注字节顺序就可以了,硬件除了要处理字节顺序外,还需要处理位序。如果你觉得Big Endian和Little Endian很难理解,可以这么理解,Big Endian就是最先读出最高(最大)的字节,而Little Endian最先读出最低(最小)的字节。
通常在TCP/IP协议栈所说的网络序(Network Order)就是遵循Big-Endian规则。在TCP/IP网络通信中,通信双方把消息按照如图1的方式进行编码,然后按从MSB(Bit0)到LSB的顺序在网络上传送;而通常我们说的主机序(Host Order)(X86架构CPU)就是遵循Little-Endian规则。所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序(Little-Endian)和网络序(Big-Endian)的转换。
了解了这些基本概念后,我们进入正题。lwip由于考虑到移植性问题,因此它没有默认主机序为Little Endian,而是两种情况都进行了处理;而且处于灵活性考虑,还允许我们用自己定义的代码替换lwip提供的函数:
#if LWIP_PLATFORM_BYTESWAP
#define htons(x) LWIP_PLATFORM_HTONS(x)
#define ntohs(x) LWIP_PLATFORM_HTONS(x)
#define htonl(x) LWIP_PLATFORM_HTONL(x)
#define ntohl(x) LWIP_PLATFORM_HTONL(x)
#else
u16_t htons(u16_t x);
u16_t ntohs(u16_t x);
u32_t htonl(u32_t x);
u32_t ntohl(u32_t x);
#endif
如果我们需要采用自己定义的函数,只需要定义LWIP_PLATFORM_BYTESWAP为1,并编写相应的函数即可:
#define LWIP_PLATFORM_BYTESWAP 1
#define LWIP_PLATFORM_HTONS(x)
#define LWIP_PLATFORM_HTONL(x)
考察了lwip实现的灵活性后,我们再来看看其移植性问题。为了便于移植,lwip引入了3个宏:BYTE_ORDER、LITTLE_ENDIAN和BIG_ENDIAN,后两个宏的定义为:
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN 4321
#endif而BYTE_ORDER由我们自己根据CPU类型来定义,如果CPU采用Big Endian,就定义为4321,反之就定义为1234。
有了这三个宏以后,代码的编写就很简单了:
#if BYTE_ORDER == BIG_ENDIAN
#define htons(x) (x)
#define ntohs(x) (x)
#define htonl(x) (x)
#define ntohl(x) (x)
#else /* BYTE_ORDER != BIG_ENDIAN */
#if LWIP_PLATFORM_BYTESWAP
#define htons(x) LWIP_PLATFORM_HTONS(x)
#define ntohs(x) LWIP_PLATFORM_HTONS(x)
#define htonl(x) LWIP_PLATFORM_HTONL(x)
#define ntohl(x) LWIP_PLATFORM_HTONL(x)
#else
u16_t htons(u16_t x);
u16_t ntohs(u16_t x);
u32_t htonl(u32_t x);
u32_t ntohl(u32_t x);
#endif
当CPU类型为Big Endian时,主机序与网络序同序,不需要改动;而CPU类型为Little Endian时,主机序与网络序正好相反,此时lwip定义了相应的函数来处理,这些函数通过移位来实现,本文以u32_t htonl(u32_t x)来说明:
u32_t
htonl(u32_t n)
{
return ((n & 0xff) << 24) |
((n & 0xff00) << 8) |
((n & 0xff0000) >> 8) |
((n & 0xff000000) >> 24);
}
上面的代码其实很简单,就是将字节进行逆序排列。
写到这里,相信大家都对lwip处理网络序和字节序的机制有了一定的了解了。