大小端序及socket通信字節序問題

大端小端序概念

講概念前,先插個小東西,之前搞混高字節、高地址、低字節、低地址這幾個概念,之後理解大小端序就費勁了些。故畫了下圖:
圖1

大端序(big-Endian):高字節保存在內存的低地址,低字節保存在內存的高地址。
小端序(little-Endian):高字節保存在內存的高地址,低字節保存在內存的低地址。

概念較抽象,可結合下圖來理解:
圖2

大小端優缺點

存在即是合理,大端的優點就是小端的缺點,反之亦然。
大端優點:符號位在所表示數據內存的第一個字節,便於快速判斷數據的正負和大小。
小端優點:內存的低地址存放數據低字節,大數強制轉換小數時效率高,直接丟棄高地址數據即可;cpu在做數值運算時依次從低到高取數運算即可,效率高效。

Socket通信字節序問題

IP/TCP網絡傳輸時採用網絡字節序(即大端序),這句話常常會被誤讀:認爲網絡傳輸時採用的是大端序。故在編寫網絡程序時,常常有困惑:在創建socket或bind時,要用htonl、htons等函數來將端口或ip地址從主機字節序轉換成網絡字節序,而在之後的send,recv等函數中爲什麼就沒有使用這些函數了?

經過一翻搜索之後,思路也算理清了。IP/TCP協議棧內部是遵照標準來的,使用大端序來解析數據,因爲端口及ip地址在拆解協議包時要使用到,因此使用者必須將它們轉化成大端序。而send,recv等函數傳輸的只是字節流,不關心大小端序,這些字節流是交給用戶層去處理的,至於用戶層如何去使用,使用的對不對,那是用戶層的事情。以下是不同cpu體系平臺間傳輸數據示意圖(用戶層未處理字節序時的情況,主要爲了說明send,recv不關心大小端序的問題,只是傳輸字節流):
圖3
IP/TCP標準說傳輸時採用網絡字節序,主要是爲了解決不同平臺之間的數據傳輸問題,如果要遵循這個標準的話,那麼在send發送數據前就要調用htonl、htons等函數將本機字節序數據轉化面網絡字節序,在recv接收數據後,就要調用ntohl、ntohs等函數將網絡字節序數據轉化成本機字節序數據了。

因爲現在大多數機器的CPU架構都是基於x86 (Intel、AMD等)體系的,故代碼中就未考慮字節序的問題了(認爲都是一樣的字節序架構體系),故在send、recv等函數中用戶層就沒有再去htonl、htons、ntohl、htohs等函數了。從這可得知,若服務端運行在小端序機器,客戶端運行在大端序機器,不考慮字節序問題的話,那結果就是不能工作了。

注:如果發送的是字符串,即在send前用sprintf等函數將數據全部轉換成字符串,recv的也是字符串,然後在用戶層自已去解析這些字符串,該轉換成數字的就用atoi等函數去轉換,那麼就不需要去考慮網絡字節序的問題了,單個字節沒有字節序的問題啦。

問題拓展

爲什麼socket底層不幫忙處理字節序的問題的,send時,socket底層將主機字節序轉換成網絡字節序,然後recv時,socket底層又將網絡字節序轉換成主機字節序?
稍加思考,不難得出,socket不可能設計成幹這事,如果要幹這事,socket底層勢必要知道數據流的結構才行。比如一個字節流由4個整數組成:2個字節整數,1個字節整數,1個字節整數,4個字節整數。用戶層程序是知道字節流結構的,故可以挨個去調用轉換,而socket 底層不知道具體的字節流構成,故無法去做轉換了。

參考

https://blog.csdn.net/u014449821/article/details/80080672
https://blog.csdn.net/fysy0000/article/details/6622549
https://blog.csdn.net/qingtian506/article/details/53750398
https://www.v2ex.com/t/330173
http://blog.chinaunix.net/uid-15014334-id-4062785.html
http://www.52rd.com/Blog/Detail_RD.Blog_imjacob_17298.html

注:以上講解完全是基於自己的理解所寫,因爲本人無大端序的機器,大端方面的未經實踐的檢驗。若讀者發現有錯誤,請務必批評指出,謝謝。

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