腦殘式網絡編程入門(九):面試必考,史上最通俗大小端字節序詳解

一、引言

最近在從頭重寫 MobileIMSDK 的TCP版,自已組織TCP數據幀時就遇到了字節序大小端問題。因此,借這個機會單獨整理了這篇文章,但願能加深你們對字節序問題的理解,增強對IM這種基於網絡通訊的程序在數據傳輸這一層的知識掌控狀況。html

程序員在寫應用層程序時,通常不須要考慮字節序問題,由於字節序跟操做系統和硬件環境有關,而咱們編寫的程序要麼不須要跨平臺(好比只運行在windows),要麼須要跨平臺時會由Java這種跨平臺語言在虛擬機層屏蔽掉了。git

但典型狀況,當你編寫網絡通訊程序,好比IM聊天應用時,就必需要考慮字節序問題,由於你的數據在這樣的場景下要跨機器、跨網絡通訊,必須解決不一樣系統、不一樣平臺的字節序問題。程序員

* 閱讀對象:本文屬於計算機基礎知識,特別適合從事網絡編程方面工做(好比IM這類通訊系統)的程序員閱讀。面視時,面視官通常都會聊到這個知識點。github

本文已同步發佈於「即時通信技術圈」公衆號,歡迎關注:面試

▲ 本文在公衆號上的連接是:點此進入 ,原文連接是:http://www.52im.net/thread-3101-1-1.html編程

二、什麼是字節序?

字節序,是指數據在內存中的存放順序,當字節數大於1時須要考慮(只有一個字節的狀況下,好比char類型,也就不存在順序問題啦)。windows

從下圖中,能夠直觀的感覺到什麼是字節序問題: 服務器

(上圖片改編自《C語言打印數據的二進制格式-原理解析與編程實現》)網絡

三、字節序的分類

字節序常被分爲兩類:函數

  • 1)Big-Endian(大端字節序):高位字節排放在內存的低地址端,低位字節排放在內存的高地址端(這是人類讀寫數值的方法);
  • 2)Little-Endian(小端字節序):低位字節排放在內存的低地址端,高位字節排放在內存的高地址端。

舉個具體的例子,0x1234567 的大端字節序和小端字節序寫法以下:

如上圖所示:大端小端字節序最小單位1字節,即8bit;大端字節序就是和咱們平時寫法的順序同樣,從低地址到高地址寫入0x01234567;而小端字節序就是和咱們平時的寫法反過來,由於字節序最小單位爲1字節,因此從低地址到高地址寫入0x67452301。

四、爲何會存在大端、小端字節序問題?

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;
}

五、「大端」、「小端」名字由來

根據網上的資料,聽說名字的由來跟喬納森·斯威夫特的著名諷刺小說《格列佛遊記》有關。

書中的故事是這樣的:通常來講,你們都認爲吃雞蛋前,原始的方法是打破雞蛋較大的一端。但是當今皇帝的祖父小時候吃雞蛋,一次按古法打雞蛋時碰巧將一個手指弄破了,所以他的父親,當時的皇帝,就下了一道敕令,命令全體臣民吃雞蛋時打破雞蛋較小的一端,違令者重罰。

小人國內部分裂成Big-endian和Little-endian兩派,區別在於一派要求從雞蛋的大頭把雞蛋打破,另外一派要求從雞蛋的小頭把雞蛋打破。

小人國國王改變了打開雞蛋的方位與理由,並由此招致了修改法律、引起戰爭和宗教改革等一序列事件的發生。

《格列佛遊記》中的這則故事,本來是藉以諷刺英國的政黨之爭。而在計算機工業中,也借用了這個故事來代指你們在數據儲存字節順序中的分歧,並把「大端」(Big-endian)、「小端」(Little-endian)的名字,沿用到了計算機中。 

(上圖片改編自《「字節序」是個什麼鬼?》)

或許,借用這個故事來命名大小端字節序問題,無非就是想告訴你們,所謂的「大端」、「小端」實際上可能無關計算機性能,更多的只是創造者們在創立計算機之初,代入了我的的一些約定俗成的習慣而已。

六、什麼是網絡字節序?

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語言中經常使用)。固然,也能夠編寫本身的轉換函數。

七、實踐中的大小端字節序處理

在我編寫MobileIMSDK的TCP版時(MobileIMSDK是我開源的IM通訊層庫),一樣遇到了大小端字節序問題。

以MobileIMSDK的iOS端拼裝網絡數據收發的代碼爲例:

如上圖代碼所示,注意如下兩個大小端轉換函數的使用:

  • 1)第27行「CFSwapInt32HostToBig」函數:網絡發出數據以前,先將主機字節序轉爲網絡字節序(即大端字節序);
  • 2)第53行「CFSwapInt32BigToHost」函數:收到原始網絡數據後,轉爲主機字節序後就能夠在程序中正常使用了。

若是對網絡大小端轉換這方面的實踐感興趣,能夠自已去下載MobileIMSDK源碼試一試:https://github.com/JackJiang2011/MobileIMSDK

八、參考資料

[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

相關文章
相關標籤/搜索