ntohl和htonl的一次误用

来源:互联网 发布:java 堆栈使用 编辑:程序博客网 时间:2024/06/06 18:44
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);uint32_t ntohl(uint32_t netlong);

以上是htonl和ntohl的原型,参数和返回值都很简单明了。我们看下man手册中对这两个函数原型的描述。

DESCRIPTIONThe htonl() function converts the unsigned integer hostlong from host byte order to network byte order.(htonl函数把无符号整型变量hostlong从本地字节序转换成网络字节序)The ntohl() function converts the unsigned integer netlong from network byte order to host byte order.(ntohl函数把无符号整型变量netlong从网络字节序转换成本地字节序)

虽然函数描述很简单,但是有一点我们是要切记的——htonl的形参必须是主机字节序的整型变量,而ntohl的形参必须是网络字节序的整型变量。

以上大家应该不以为意,毕竟做过socket开发的程序猿都知道,网络传送过来的大小大于1个字节的变量都要用ntohs或ntohl等转换成主机字节序,要通过网络发送的也要将大小大于1个字节的变量转换成网络字节序。

而恰恰,在开发中,碰到了这种或许是粗心的调用,如下:

int pkgLen = sizeof(int) + htonl(pHeader->length);

我们只看htonl(pHeader->length),pHeader->length是一个int类型的变量,它是从传输过来的网络字节流读取的四个字节,我们假设该四个字节的网络字节序的十六进制是”12 34 56 00”,主机机器是小端的。那么,我们实际上应该是这么调用:

int pkgLen = sizeof(int) + ntohl(pHeader->length); --[1]

但是某个开发同学不小心这么写了:

int pkgLen = sizeof(int) + htonl(pHeader->length); --[2]

此时,结果会是怎么样的呢?[1]和[2]的结果恰好是一样的。为什么?

因为ntohl和htonl都是先判断主机字节序和网络字节序是否一样(即判断主机机器为大端或小端,网络字节序是大端格式的),若不一样则对形参进行首尾字节互换操作并返回,一样则直接返回。

由于上述假设主机机器为小端,和网络字节序不一样,因此无论是调用htonl还是ntohl,都会进行首尾字节互换的操作。因为我们的实参是大端格式的数据,所以经过互换后,就变成小端格式的数据,也就是跟主机机器一样的多字节存储格式。

基于以上原因,[1]和[2]的结果是一样且正确的,但是谨记[1]才是正确的使用方法,因为[2]实际上必须传入主机字节序的变量,但pHeader->length是网络字节序。

针对这个问题,再给出测试例子如下:

#include <stdio.h>#include <arpa/inet.h>int main(int argc, char** argv){    // test the machine is little-endian or big-endian    int data, *point;    data = 0;    point = &data;    *point = 0x21;//big-endian: 21 00 00 00,little-endian: 00 00 00 21    if(0x21 == data)        printf("little endian\n");    else if(data == 0x21000000)        printf("big endian\n");    else        printf("error data\n");    int netData = htonl(0x12345600);    int h2nData = htonl(netData);    int n2hData = ntohl(netData);    printf("netData[0x%x] h2nData[0x%x] n2hData[0x%x]\n", netData, h2nData, n2hData);    return 0;}

输出结果如下:

little endiannetData[0x563412] h2nData[0x12345600] n2hData[0x12345600]

结果一样是万幸!我们且需记住htonl和ntohl的参数应该是传什么!

0 0
原创粉丝点击