首先解釋一下大小端的概念。
大端(big endian),同時也是網路序,是資料在網路上傳輸的一種資料組織格式,其儲存的方式比較符合人們讀寫的習慣。
小端(little endian),這裡不能說其是主機序,因為主機可能採用的是大端cpu也可能採用的是小端cpu,小端與大端相對。
通過乙個例子深入了解它們之間的區別:
用一台big endian 和 另一台 little endian 分別儲存32bit的數值,為:0x12345678,資料在記憶體當中的儲存順序如下表所示。
addr
big endian
little endian
0xa0x12
0x78
0xb0x34
0x56
0xc0x56
0x34
0xd0x78
0x12
由表1可以清晰看到,在大端;在小端計算機當中,可以總結為八個字:小端(低位元組)存在起始位址。我們深入了解位元組在計算機當中的儲存方式,仍以0x12345678為例。
32 bit representation in memory:
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行
從**當中可以看到,針對不同的編譯器版本,提供了不同的位元組轉換,有使用編譯器內建的函式,也有使用彙編實現的位元組序的轉換,同時也有**自己實現的__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,結果符合我們的猜想。
從網路序轉換為主機序的分析思路與此完全一致,有興趣的盆友可自行動手試探一番。
好啦,位元組序轉換初步寫到這裡,這裡僅僅介紹了位元組間的轉換,並沒有分析跨位元組位域怎麼儲存和我們怎麼轉換和存值,欲知後事,請聽下回分析~
網路程式設計之大小端
在計算機的資料儲存中,有2種的儲存方式,就是大小端,大端指的是高位資料儲存在低位的位址,低位資料放高位,小端則相反,我們了解清這一點很重要,因為要保證資料的一致性,傳送端和接收端就應該協商好用哪種儲存方式來傳送和接收。include includeusing namespace std int ma...
linux網路程式設計 初探TCP
伺服器端等待客戶端連線,連線成功後,列印客戶端的ip和port,然後迴圈接收資料,緩衝區無資料就阻塞待待。include include include include include include include include include include define maxbuf 10 ...
Socket網路程式設計之Client端
socket inetaddress string remoteaddress,int port 建立連線到指定遠端主機 遠端埠的socket,該構造器沒有指定本地位址 本地埠,預設使用本地主機的預設ip位址,預設使用系統動態指定的ip位址。socket inetaddress string rem...