天天看點

主機位元組序和網絡位元組序

  • 英文小冊原文位址:​​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​

​是存儲在了記憶體的高位址處,還是記憶體的低位址處呢?

這種位元組的排列順序就叫做位元組序,位元組序有兩種:

  1. 大端位元組序

數值的低位元組放在記憶體的低位址處,數值的高位元組放在記憶體的高位址。

  1. 小端位元組序

數值的低位元組放在記憶體的高位址處,數值的高位元組放在記憶體的低位址。

主機位元組序

當我們談論位元組序,大部分時候對應的都是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