1、引言
最近在從頭重寫 MobileIMSDK 的TCP版,自已組織TCP數據幀時就遇到了字節序大小端問題。所以,借這個機會單獨整理了這篇文章,希望能加深大家對字節序問題的理解,加強對IM這種基于網絡通信的程序在數據傳輸這一層的知識掌控情況。
程序員在寫應用層程序時,一般不需要考慮字節序問題,因為字節序跟操作系統和硬件環境有關,而我們編寫的程序要么不需要跨平臺(比如只運行在windows),要么需要跨平臺時會由Java這種跨平臺語言在虛擬機層屏蔽掉了。
但典型情況,當你編寫網絡通信程序,比如IM聊天應用時,就必須要考慮字節序問題,因為你的數據在這樣的場景下要跨機器、跨網絡通信,必須解決不同系統、不同平臺的字節序問題。
* 閱讀對象:本文屬于計算機基礎知識,特別適合從事網絡編程方面工作(比如IM這類通信系統)的程序員閱讀。面視時,面視官一般都會聊到這個知識點。
本文已同步發布于“即時通訊技術圈”公眾號,歡迎關注:
▲ 本文在公眾號上的鏈接是:點此進入 ,原文鏈接是:http://www.52im.net/thread-3101-1-1.html
2、什么是字節序?
字節序,是指數據在內存中的存放順序,當字節數大于1時需要考慮(只有一個字節的情況下,比如char類型,也就不存在順序問題啦)。
從下圖中,可以直觀的感受到什么是字節序問題:
(上圖片改編自《C語言打印數據的二進制格式-原理解析與編程實現》)
3、字節序的分類
字節序常被分為兩類:
- 1)Big-Endian(大端字節序):高位字節排放在內存的低地址端,低位字節排放在內存的高地址端(這是人類讀寫數值的方法);
- 2)Little-Endian(小端字節序):低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。
舉個具體的例子,0x1234567 的大端字節序和小端字節序寫法如下:
如上圖所示:大端小端字節序最小單位1字節,即8bit;大端字節序就是和我們平時寫法的順序一樣,從低地址到高地址寫入0x01234567;而小端字節序就是和我們平時的寫法反過來,因為字節序最小單位為1字節,所以從低地址到高地址寫入0x67452301。
4、為什么會存在大端、小端字節序問題?
4.1 比較合理的解釋
一個比較合理的解釋是說:計算機中電路優先處理低位字節,效率比較高,因為計算機都是從低位開始的,所以計算機內部處理都是小端字節序。
而人類人類讀寫數值的方法,習慣用大端字節序,所以除了計算機的內部處,其他的場理合都是大端字節序,比如:網絡傳輸和文件儲存時都是用的大端字節序(關于網絡字節序,會在后面繼續展開說明)。
大小端字節序問題,最有可能是跟技術算硬件或軟件的創造者們,在技術創立之初的一些技術條件或個人習慣有關。
所以大小端問題,體現在實際的計算機工業應用來上,不同的操作系統和不同的芯片類型可能都會有不同。
4.2 常見的操作系統和芯片使用的字節序
具體來說:DEC和Intel的機器(X86平臺)一般采用小端,IBM、Motorola(Power PC)、Sun的機器一般采用大端。
當然,這不代表所有情況。有的CPU即能工作于小端, 又能工作于大端,比如:Arm、Alpha、摩托羅拉的PowerPC。
而且,具體這類CPU是大端還是小端,和具體設置也有關。如:Power PC支持小端字節序,但在默認配置時是大端字節序。
一般來說:大部分用戶的操作系統(如:Windows、FreeBsd、Linux)是小端字節序。少部分,如:Mac OS 是大端字節序。
4.3 如何判斷用的是什么字節序?
怎么判斷我的計算機里使用的是大端還是小端字節序呢?
下面的這段代碼可以用來判斷計算機是大端的還是小端。判斷的思路是:確定一個多字節的值(下面使用的是4字節的整數),將其寫入內存(即賦值給一個變量),然后用指針取其首地址所對應的字節(即低地址的一個字節),判斷該字節存放的是高位還是低位,高位說明是Big endian,低位說明是Little endian。
#include <stdio.h>
int main ()
{
unsigned int x = 0x12345678;
char*c = (char*)&x;
if(*c == 0x78) {
printf("Little endian");
} else{
printf("Big endian");
}
return 0;
}
5、“大端”、“小端”名字由來
根據網上的資料,據說名字的由來跟喬納森·斯威夫特的著名諷刺小說《格列佛游記》有關。
書中的故事是這樣的:一般來說,大家都認為吃雞蛋前,原始的方法是打破雞蛋較大的一端。可是當今皇帝的祖父小時候吃雞蛋,一次按古法打雞蛋時碰巧將一個手指弄破了,因此他的父親,當時的皇帝,就下了一道敕令,命令全體臣民吃雞蛋時打破雞蛋較小的一端,違令者重罰。
小人國內部分裂成Big-endian和Little-endian兩派,區別在于一派要求從雞蛋的大頭把雞蛋打破,另一派要求從雞蛋的小頭把雞蛋打破。
小人國國王改變了打開雞蛋的方位與理由,并由此招致了修改法律、引發戰爭和宗教改革等一序列事件的發生。
《格列佛游記》中的這則故事,原本是借以諷刺英國的政黨之爭。而在計算機工業中,也借用了這個故事來代指大家在數據儲存字節順序中的分歧,并把“大端”(Big-endian)、“小端”(Little-endian)的名字,沿用到了計算機中。
(上圖片改編自《“字節序”是個什么鬼?》)
或許,借用這個故事來命名大小端字節序問題,無非就是想告訴大家,所謂的“大端”、“小端”實際上可能無關計算機性能,更多的只是創造者們在創立計算機之初,代入了個人的一些約定俗成的習慣而已。
6、什么是網絡字節序?
6.1 字節序問題給網絡通信帶來的困擾
對于搞網絡通信應用(比如IM、消息推送、實時音視頻)開發的程序員來說,自已寫通信底層的話是一定會遇到大小端問題的,對于網絡字節序這個知識點是一定要必知必會。(當然,你要是很沒追求的認為,反正我公司就讓租租第3方,能用就行,具體通底層怎么寫我才不想掉頭發去考慮那么多。。。。 那哥也救不了你。。)
上面所說的大小端字節序都是在說計算機自己,也被稱作主機字節序。同型號計算機上寫的程序,在相同的系統上面運行總歸是沒有問題。
但計算機網絡的出現讓大小端問題變的復雜化了,因為每個計算機都有自己的主機字節序。不同計算機之間通過網絡通信時:我“說”的你聽不懂,你“說”我也聽不懂,這可怎么辦?
6.2 TCP/IP協議強行約定了字節序方案
好消息是,TCP/IP協議很好的解決了這個問題,TCP/IP協議規定使用“大端”字節序作為網絡字節序。
這樣,即使不使用大端的計算機也沒有關系,因為發送數據的時候可以將自己的主機字節序轉換為網絡字節序(即“大端”字節序),對接收到的數據轉換為自己的主機字節序。這樣一來,也就達到了與CPU、操作系統無關,實現了網絡通信的標準化。
具體的原理就是:
- 1)TCP/IP協議會把接收到的第一個字節當作高位字節看待,這就要求發送端發送的第一個字節是高位字節;
- 2)而在發送端發送數據時,發送的第一個字節是該數值在內存中的起始地址處對應的那個字節。
也就是說,該數值在內存中的起始地址處對應的那個字節就是要發送的第一個高位字節(即:高位字節存放在低地址處)。由此可見,多字節數值在發送之前,在內存中就是以大端法存放的。
所以說,網絡字節序就是大端字節序。
6.3 主機字機序到網絡字節序的轉換
那么,為了程序的兼容,程序員們每次發送和接受數據都要進行轉換,這樣做的目的是保證代碼在任何計算機上執行時都能達到預期的效果。
通信時的這種常用的操作,Socket API這一層,一般都提供了封裝好的轉換函數,方便程序員使用。比如從主機字節序到網絡字節序的轉換函數:htons、htonl(C語言中常用),從網絡字節序到主機字節序的轉換函數:ntohs、ntohl(C語言中常用)。當然,也可以編寫自己的轉換函數。
7、實踐中的大小端字節序處理
在我編寫MobileIMSDK的TCP版時(MobileIMSDK是我開源的IM通信層庫),同樣遇到了大小端字節序問題。
以MobileIMSDK的iOS端拼裝網絡數據收發的代碼為例:
如上圖代碼所示,注意以下兩個大小端轉換函數的使用:
- 1)第27行“CFSwapInt32HostToBig”函數:網絡發出數據之前,先將主機字節序轉為網絡字節序(即大端字節序);
- 2)第53行“CFSwapInt32BigToHost”函數:收到原始網絡數據后,轉為主機字節序后就可以在程序中正常使用了。
如果對網絡大小端轉換這方面的實踐感興趣,可以自已去下載MobileIMSDK源碼試一試:https://github.com/JackJiang2011/MobileIMSDK。
8、參考資料
[1] “字節序”是個什么鬼?
[2] 大小端及網絡字節序
[3] C語言打印數據的二進制格式-原理解析與編程實現
附錄:系列文章
本文是系列文章中的第9篇,本系列大綱如下:
《腦殘式網絡編程入門(一):跟著動畫來學TCP三次握手和四次揮手》
《腦殘式網絡編程入門(二):我們在讀寫Socket時,究竟在讀寫什么?》
《腦殘式網絡編程入門(三):HTTP協議必知必會的一些知識》
《腦殘式網絡編程入門(四):快速理解HTTP/2的服務器推送(Server Push)》
《腦殘式網絡編程入門(五):每天都在用的Ping命令,它到底是什么?》
《腦殘式網絡編程入門(六):什么是公網IP和內網IP?NAT轉換又是什么鬼?》
《腦殘式網絡編程入門(七):面視必備,史上最通俗計算機網絡分層詳解》
《腦殘式網絡編程入門(八):你真的了解127.0.0.1和0.0.0.0的區別?》
《腦殘式網絡編程入門(九):面試必考,史上最通俗大小端字節序詳解》(本文)
(本文同步發布于:http://www.52im.net/thread-3101-1-1.html)