- 英文小冊原文位址:beej.us/guide/bgnet…
- 作者:Beej
- 中文翻譯位址:www.chanmufeng.com/posts/netwo…
現将目錄貼下:
- 什麼是socket
- 兩種Socket
- 漫談網絡
- IP位址、struct以及位址轉換
- IP位址,IPv4和IPv6
- 子網
- 位元組序
- struct結構
- 再談IP位址
- 從IPv4遷移到IPv6
- IP位址,IPv4和IPv6
- 位元組序
- struct結構
- 再談IP位址
- System call 或 Bust
- getaddrinfo()—準備開始!
- socket()—拿到檔案描述符!
- bind()—我在監聽哪個端口?
- connect()—嘿,你好啊!
- listen()—會有人聯系我嗎?
- accept()—感謝呼叫3490端口
- send() and recv()—跟我唠唠吧,寶兒!
- sendto() and recvfrom()—用DGRAM風格跟我說話
- close() and shutdown()—滾犢子!
- getpeername()—你哪位?
- gethostname()—我是誰?
- Client-Server基礎
- 一個簡單的流伺服器
- 一個簡單的流用戶端
- Datagram Sockets
- 技術進階
- Blocking—何謂阻塞?
- poll()—同步的I/O多路複用
- select()—老古董的同步I/O多路複用
- 資料隻傳了一部分怎麼辦?
- Serialization-如何封裝資料?
- 資料封裝
- 廣播資料包-大聲說「Hello,World」
什麼是位元組序
位元組是記憶體讀寫的最小機關,一個位元組能夠表示的資料範圍是0~255,也就是說如果你要儲存一個在此範圍區間的數字,一個位元組足矣。
但是像占4個位元組的
int
,8個位元組的
double
,該怎麼存儲呢?多個位元組該用怎樣的順序來進行排列?舉個例子,
0xb3f4
,數值的高位
b3
是存儲在了記憶體的高位址處,還是記憶體的低位址處呢?
這種位元組的排列順序就叫做位元組序,位元組序有兩種:
- 大端位元組序
數值的低位元組放在記憶體的低位址處,數值的高位元組放在記憶體的高位址。
- 小端位元組序
數值的低位元組放在記憶體的高位址處,數值的高位元組放在記憶體的低位址。
主機位元組序
當我們談論位元組序,大部分時候對應的都是CPU通路記憶體時的概念,比如對下圖而言:

記憶體低位存儲的位元組是
0xb3
,高位存儲的是
0xf4
,至于CPU讀取這個2位元組資料的時候,是将其解釋為
0xb3f4
(小端)還是
0xf4b3
(大端)就取決于CPU采用的是哪一種位元組序了。
常用的CPU位元組序如下:
- 大端位元組序:IBM、PowerPC
- 小端位元組序:x86
這種CPU位元組序也被稱為主機位元組序(Host Byte Order) 。翻譯一下就是,IBM的主機序是大端,x86的主機序是小端。
問題來了。
世界上的主機這麼多,每台主機的CPU類型還不一樣,A主機按照A的主機位元組序發資訊給B主機,B主機如果直接按照B的主機位元組序來解析資訊,那麼極有可能會産生錯誤。
要想解決這個問題,還必須引出一些其他情況下的位元組序。
為了避免引起混淆,我在這裡強調一下,位元組序就分為2種,大端和小端。而所謂的主機位元組序以及下面我将提到的兩種,都隻是位元組序作用在不同的場景中取得特定名稱罷了。
檔案存儲位元組序
顧名思義,就是存儲檔案資訊的時候用的是大端還是小端。
bmp格式的圖檔屬于小端位元組序,jpeg格式的圖檔就是大端位元組序。
這裡提及檔案存儲位元組序主要是為了讓大家了解一個事實:采用什麼位元組序完全是開發者設計産品時的一種技術選型罷了。但是這種選型一定要成為一種标準,讓其他開發者解析的時候明白應該用哪種位元組序,否則jpeg的圖檔也就沒辦法在所有的電腦上正常顯示了,你說對嗎?
現在好了,所有jpeg軟體的開發者都知道應該用大端位元組序來儲存jpeg圖檔,但是對于從其他主機傳過來的jpeg資料流,開發者又怎麼知道這個資料流用的是大端還是小端呢?
這就是網絡位元組序(Network Byte Order) 的問題了。
網絡位元組序
我想你應該能想明白了,網絡位元組序不是大端就是小端,不可能存在「可能是大端也可能是小端」這種情況,否則網絡傳遞的消息全都亂套了!
TCP/IP既然是一種标準,那标準自然就有自己的位元組序規定。
TCP/IP網絡采用的是大端,是規定好的一種資料表示格式,它與具體的CPU類型、作業系統等無關,進而可以保證資料在不同主機之間傳輸時能夠被正确解釋。
也就是說,如果你想讓你的消息能在其他裝置正常解析,就必須按照大端位元組序進行處理。比如,網絡傳輸的是
0x12345678
這個整形變量,首先被發送的應該是
0x12
,接着是
0x34
,然後是
0x56
,最後是
0x78
。
位元組序在socket中的例子
當你在網絡封包或者在填充資料的時候,你需要确定你的端口号(port number)和ip位址都是符合網絡位元組序的。但是你并不知道你的主機位元組序是大端還是小端,你自然也就搞不清你到底需不需要進行轉換。
其實不必如此費神,不用在意主機序,直接調用函數強行将主機序轉換為網絡位元組序即可。下面舉一個C語言編寫伺服器代碼綁定端口的例子。
代碼中被紅色标記的部分就是分别将端口号
33000
由主機序轉換為網絡位元組序,将ip位址由主機序轉換為網絡位元組序的函數。
乍眼一看,函數名非常古怪,不好記憶,下面稍微解釋一下,各位就豁然開朗了。
Socket函數庫
提供了這種位元組序轉換的函數,函數的作用對象有兩種,分别是
short
(2位元組)和
long
(4位元組),這就是函數最後的「
s
」和「
l
」的含義。
還有,主機位元組序的英文是
Host Byte Order
,簡寫為「
h
」,網絡位元組序的英文是
Network Byte Order
,簡寫為「
n
」,「
to
」表示轉換,是以,如果你想将
short
資料從主機位元組序轉換為網絡位元組序,那就應該是
h-to-n-s
,表示
Host to Network Short
。是不是很簡單?
是以理論上,你可以用「
h
」、「
to
」、「
n
」、「
s/l
」這幾個字母靈活組合,組成你想要的api,但是也别瞎搞,stolh()["Short to Long Host"]這種組合壓根都不存在。
基本上你需要的也就是以下四種罷了:
htons() // host to network short
htonl() // host to network long
ntohs() // network to host short
ntohl() // network to host long