ntohl和htonl的一次誤用

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint32_t ntohl(uint32_t netlong);

以上是htonl和ntohl的原型,參數和返回值都很簡單明瞭。我們看下man手冊中對這兩個函數原型的描述。

DESCRIPTION

The 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 endian
netData[0x563412] h2nData[0x12345600] n2hData[0x12345600]

結果一樣是萬幸!我們且需記住htonl和ntohl的參數應該是傳什麼!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章