1. 程式人生 > >Linux網路程式設計 之 大小端初探

Linux網路程式設計 之 大小端初探

    首先解釋一下大小端的概念。

    大端(Big Endian),同時也是網路序,是資料在網路上傳輸的一種資料組織格式,其儲存的方式比較符合人們讀寫的習慣。

    小端(Little Endian),這裡不能說其是主機序,因為主機可能採用的是大端cpu也可能採用的是小端cpu,小端與大端相對。

    通過一個例子深入瞭解它們之間的區別:

    用一臺Big Endian 和 另一臺 Little Endian 分別儲存32bit的數值,為:0x12345678,資料在記憶體當中的儲存順序如下表所示。

Addr

Big Endian

Little Endian

0xa

0x12

0x78

0xb

0x34

0x56

0xc

0x56

0x34

0xd

0x78

0x21

    由表1可以清晰看到,在大端計算機當中,儲存方式可以總結為八個字:大端(高位元組)存在起始地址;在小端計算機當中,可以總結為八個字:小端(低位元組)存在起始地址我們深入瞭解位元組在計算機當中的儲存方式,仍以0x12345678為例。

    32 bit representation in memory:

  • Big Endian:

    Hex: 0x12345678

    Binary: 00010010 00110100 01010110 01111000

  • Little Endian

    Hex: 0x78563412

    Binary: 01111000 01010110 00110100 00010010

    Decimal: 305419896

    從上面的資料分佈,可以發現一個規律,不論大小端

,他們儲存位元組,位元組當中的bit位相對順序一樣的,由此,給我們大小端互相轉換提供了思路,利用位運算和移位運算。實際上linux已經為我們提供了位元組序轉換的api,我們可以在linux終端下令入:man htonl 即可檢視到使用此函式所需要包含的標頭檔案,用法等,鍵入後得到結果如下圖所示:

    

    現在我們去找一下htonl 的linux實現程式碼,由於linux的標頭檔案基本都在/usr/include下,因此我們直接利用linux提供的find工具去進行搜尋:在終端鍵入

    find /usr/include/ -type f | xargs grep -n htonl

    得到結果如下:

紅色的兩句對我們而言是比較有用的兩句,從上面可以看到,htonl其實是__int32_identify 和 __bsway_32的巨集替換,我們鍵入命令開啟檔案 /usr/include/netinet/in.h檢視並定位到行397(vim 下鍵入":"並輸入行號即可定位到相應行)。

vim /usr/include/netinet/in.h

得到如下結果:

得知一個是用於大端的轉換(其實大端已經沒有必要轉,網路流進主機的資料,就是大端的)我們可以猜想,這個__int32_identify是一個類似 #define__int32_identify(x) x空的巨集定義或者一個直接將傳入引數返回的函式,事實證明ubuntu中採用的的是後者,並且使用了內連函式。我們繼續查詢__bsway_32,因為這個函式的實現才是我們所關心的,在終端鍵入:

find /usr/include/ -type f | xargs grep -n __bswap_32

得到如下結果:

紅色框起來的部分資訊就是我們所尋找的資訊,開啟檔案byteswap.h並定位到45行

vim /usr/include/x86_64-linux-gnu/bits/byteswap.h

從程式碼當中可以看到,針對不同的編譯器版本,提供了不同的位元組轉換,有使用編譯器內建的函式,也有使用匯編實現的位元組序的轉換,同時也有程式碼自己實現的__bswap_constant_32(x),這也正是我們所關注的,可以看到,這個巨集定義的實現,正是採用移位運算和位運算完成位元組序的轉換。

#define __bswap_constant_32(x) \

((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \

(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))

我們分析一下這個巨集定義,例如小端上儲存的為0x78563412,由上面的分析得知網路序其實是0x12345678,我們用這個函式證實一下:

0x78563412 & 0xff000000 值為 0x78000000 ,左移24bit即 0x00000078

0x78563412 & 0x00ff0000 值為 0x00560000 ,左移 8bit 即 0x00005600

0x78563412 & 0x0000ff00 值為 0x00003400 ,右移 8bit 即 0x00340000

0x78563412 & 0x000000ff 值為 0x00000012 ,右移24bit即 0x12000000

最後將右側的結果按位與得到結果0x12345678,結果符合我們的猜想。

從網路序轉換為主機序的分析思路與此完全一致,有興趣的盆友可自行動手試探一番。

好啦,位元組序轉換初步寫到這裡,這裡僅僅介紹了位元組間的轉換,並沒有分析跨位元組位域怎麼儲存和我們怎麼轉換和存值,欲知後事,請聽下回分析~