快速理解網路通訊協議

IT技術精選文摘發表於2018-05-19

1、正文引言


我們每天使用網際網路,你是否想過,它是如何實現的?

全世界幾十億臺電腦,連線在一起,兩兩通訊。上海的某一塊網路卡送出訊號,洛杉磯的另一塊網路卡居然就收到了,兩者實際上根本不知道對方的物理位置,你不覺得這是很神奇的事情嗎?

網際網路的核心是一系列協議,總稱為"網際網路協議"(Internet Protocol Suite)。它們對電腦如何連線和組網,做出了詳盡的規定。理解了這些協議,就理解了網際網路的原理。

下面就是我的學習筆記。因為這些協議實在太複雜、太龐大,我想整理一個簡潔的框架,幫助自己從總體上把握它們。為了保證簡單易懂,我做了大量的簡化,有些地方並不全面和精確,但是應該能夠說清楚網際網路的原理。

2、內容概述


2.1五層模型


網際網路的實現,分成好幾層。每一層都有自己的功能,就像建築物一樣,每一層都靠下一層支援。使用者接觸到的,只是最上面的一層,根本沒有感覺到下面的層。要理解網際網路,必須從最下層開始,自下而上理解每一層的功能。

如何分層有不同的模型,有的模型分七層,有的分四層。我覺得,把網際網路分成五層,比較容易解釋:
 

如上圖所示,最底下的一層叫做"實體層"(Physical Layer),最上面的一層叫做"應用層"(Application Layer),中間的三層(自下而上)分別是"連結層"(Link Layer)、"網路層"(Network Layer)和"傳輸層"(Transport Layer)。越下面的層,越靠近硬體;越上面的層,越靠近使用者。

它們叫什麼名字,其實並不重要。只需要知道,網際網路分成若干層就可以了。

2.2層與協議


每一層都是為了完成一種功能。為了實現這些功能,就需要大家都遵守共同的規則。大家都遵守的規則,就叫做"協議"(protocol)。

網際網路的每一層,都定義了很多協議。這些協議的總稱,就叫做"網際網路協議"(Internet Protocol Suite)。它們是網際網路的核心,下面介紹每一層的功能,主要就是介紹每一層的主要協議。

3、實體層


我們從最底下的一層開始。

電腦要組網,第一件事要幹什麼?當然是先把電腦連起來,可以用光纜、電纜、雙絞線、無線電波等方式。

 

這就叫做"實體層",它就是把電腦連線起來的物理手段。它主要規定了網路的一些電氣特性,作用是負責傳送0和1的電訊號。

4、連結層


4.1定義


單純的0和1沒有任何意義,必須規定解讀方式:多少個電訊號算一組?每個訊號位有何意義?

這就是"連結層"的功能,它在"實體層"的上方,確定了0和1的分組方式。

4.2乙太網協議


早期的時候,每家公司都有自己的電訊號分組方式。逐漸地,一種叫做"乙太網"(Ethernet)的協議,佔據了主導地位。

乙太網規定,一組電訊號構成一個資料包,叫做"幀"(Frame)。每一幀分成兩個部分:標頭(Head)和資料(Data)。

 

"標頭"包含資料包的一些說明項,比如傳送者、接受者、資料型別等等;"資料"則是資料包的具體內容。

"標頭"的長度,固定為18位元組。"資料"的長度,最短為46位元組,最長為1500位元組。因此,整個"幀"最短為64位元組,最長為1518位元組。如果資料很長,就必須分割成多個幀進行傳送。

4.3MAC地址


上面提到,乙太網資料包的"標頭",包含了傳送者和接受者的資訊。那麼,傳送者和接受者是如何標識呢?

乙太網規定,連入網路的所有裝置,都必須具有"網路卡"介面。資料包必須是從一塊網路卡,傳送到另一塊網路卡。網路卡的地址,就是資料包的傳送地址和接收地址,這叫做MAC地址。

 

每塊網路卡出廠的時候,都有一個全世界獨一無二的MAC地址,長度是48個二進位制位,通常用12個十六進位制數表示。

 

前6個十六進位制數是廠商編號,後6個是該廠商的網路卡流水號。有了MAC地址,就可以定位網路卡和資料包的路徑了。

4.4廣播


定義地址只是第一步,後面還有更多的步驟:

  • 1)首先:一塊網路卡怎麼會知道另一塊網路卡的MAC地址?
    回答是有一種ARP協議,可以解決這個問題。這個留到後面介紹,這裡只需要知道,乙太網資料包必須知道接收方的MAC地址,然後才能傳送。

  • 2)其次:就算有了MAC地址,系統怎樣才能把資料包準確送到接收方?
    回答是乙太網採用了一種很"原始"的方式,它不是把資料包準確送到接收方,而是向本網路內所有計算機傳送,讓每臺計算機自己判斷,是否為接收方。


 

上圖中,1號計算機向2號計算機傳送一個資料包,同一個子網路的3號、4號、5號計算機都會收到這個包。它們讀取這個包的"標頭",找到接收方的MAC地址,然後與自身的MAC地址相比較,如果兩者相同,就接受這個包,做進一步處理,否則就丟棄這個包。這種傳送方式就叫做"廣播"(broadcasting)。

有了資料包的定義、網路卡的MAC地址、廣播的傳送方式,"連結層"就可以在多臺計算機之間傳送資料了。

5、網路層


5.1網路層的由來


乙太網協議,依靠MAC地址傳送資料。理論上,單單依靠MAC地址,上海的網路卡就可以找到洛杉磯的網路卡了,技術上是可以實現的。

但是,這樣做有一個重大的缺點。乙太網採用廣播方式傳送資料包,所有成員人手一"包",不僅效率低,而且侷限在傳送者所在的子網路。也就是說,如果兩臺計算機不在同一個子網路,廣播是傳不過去的。這種設計是合理的,否則網際網路上每一臺計算機都會收到所有包,那會引起災難。

網際網路是無數子網路共同組成的一個巨型網路,很像想象上海和洛杉磯的電腦會在同一個子網路,這幾乎是不可能的。

 

因此,必須找到一種方法,能夠區分哪些MAC地址屬於同一個子網路,哪些不是。如果是同一個子網路,就採用廣播方式傳送,否則就採用"路由"方式傳送。("路由"的意思,就是指如何向不同的子網路分發資料包,這是一個很大的主題,本文不涉及。)遺憾的是,MAC地址本身無法做到這一點。它只與廠商有關,與所處網路無關。

這就導致了"網路層"的誕生。它的作用是引進一套新的地址,使得我們能夠區分不同的計算機是否屬於同一個子網路。這套地址就叫做"網路地址",簡稱"網址"。

於是,"網路層"出現以後,每臺計算機有了兩種地址,一種是MAC地址,另一種是網路地址。兩種地址之間沒有任何聯絡,MAC地址是繫結在網路卡上的,網路地址則是管理員分配的,它們只是隨機組合在一起。

網路地址幫助我們確定計算機所在的子網路,MAC地址則將資料包送到該子網路中的目標網路卡。因此,從邏輯上可以推斷,必定是先處理網路地址,然後再處理MAC地址。

5.2IP協議


規定網路地址的協議,叫做IP協議。它所定義的地址,就被稱為IP地址。目前,廣泛採用的是IP協議第四版,簡稱IPv4。
IPv4這個版本規定,網路地址由32個二進位制位組成:
 

習慣上,我們用分成四段的十進位制數表示IP地址,從0.0.0.0一直到255.255.255.255

網際網路上的每一臺計算機,都會分配到一個IP地址。這個地址分成兩個部分,前一部分代表網路,後一部分代表主機。比如,IP地址172.16.254.1,這是一個32位的地址,假定它的網路部分是前24位(172.16.254),那麼主機部分就是後8位(最後的那個1)。處於同一個子網路的電腦,它們IP地址的網路部分必定是相同的,也就是說172.16.254.2應該與172.16.254.1處在同一個子網路。

但是,問題在於單單從IP地址,我們無法判斷網路部分。還是以172.16.254.1為例,它的網路部分,到底是前24位,還是前16位,甚至前28位,從IP地址上是看不出來的。

那麼,怎樣才能從IP地址,判斷兩臺計算機是否屬於同一個子網路呢?這就要用到另一個引數"子網掩碼"(subnet mask)。

所謂"子網掩碼",就是表示子網路特徵的一個引數。它在形式上等同於IP地址,也是一個32位二進位制數字,它的網路部分全部為1,主機部分全部為0。比如,IP地址172.16.254.1,如果已知網路部分是前24位,主機部分是後8位,那麼子網路掩碼就是11111111.11111111.11111111.00000000,寫成十進位制就是255.255.255.0

知道"子網掩碼",我們就能判斷,任意兩個IP地址是否處在同一個子網路。方法是將兩個IP地址與子網掩碼分別進行AND運算(兩個數位都為1,運算結果為1,否則為0),然後比較結果是否相同,如果是的話,就表明它們在同一個子網路中,否則就不是。

比如,已知IP地址172.16.254.1和172.16.254.233的子網掩碼都是255.255.255.0,請問它們是否在同一個子網路?兩者與子網掩碼分別進行AND運算,結果都是172.16.254.0,因此它們在同一個子網路。

總結一下,IP協議的作用主要有兩個,一個是為每一臺計算機分配IP地址,另一個是確定哪些地址在同一個子網路。

5.3IP資料包


根據IP協議傳送的資料,就叫做IP資料包。不難想象,其中必定包括IP地址資訊。但是前面說過,乙太網資料包只包含MAC地址,並沒有IP地址的欄位。那麼是否需要修改資料定義,再新增一個欄位呢?

回答是不需要,我們可以把IP資料包直接放進乙太網資料包的"資料"部分,因此完全不用修改乙太網的規格。這就是網際網路分層結構的好處:上層的變動完全不涉及下層的結構。

具體來說,IP資料包也分為"標頭"和"資料"兩個部分:
 

"標頭"部分主要包括版本、長度、IP地址等資訊,"資料"部分則是IP資料包的具體內容。它放進乙太網資料包後,乙太網資料包就變成了下面這樣:
 

IP資料包的"標頭"部分的長度為20到60位元組,整個資料包的總長度最大為65,535位元組。因此,理論上,一個IP資料包的"資料"部分,最長為65,515位元組。前面說過,乙太網資料包的"資料"部分,最長只有1500位元組。因此,如果IP資料包超過了1500位元組,它就需要分割成幾個乙太網資料包,分開傳送了。

5.4ARP協議


關於"網路層",還有最後一點需要說明。因為IP資料包是放在乙太網資料包裡傳送的,所以我們必須同時知道兩個地址,一個是對方的MAC地址,另一個是對方的IP地址。通常情況下,對方的IP地址是已知的(後文會解釋),但是我們不知道它的MAC地址。

所以,我們需要一種機制,能夠從IP地址得到MAC地址。

這裡又可以分成兩種情況:

  • 1)第一種情況:如果兩臺主機不在同一個子網路,那麼事實上沒有辦法得到對方的MAC地址,只能把資料包傳送到兩個子網路連線處的"閘道器"(gateway),讓閘道器去處理;

  • 2)第二種情況:如果兩臺主機在同一個子網路,那麼我們可以用ARP協議,得到對方的MAC地址。ARP協議也是發出一個資料包(包含在乙太網資料包中),其中包含它所要查詢主機的IP地址,在對方的MAC地址這一欄,填的是FF:FF:FF:FF:FF:FF,表示這是一個"廣播"地址。它所在子網路的每一臺主機,都會收到這個資料包,從中取出IP地址,與自身的IP地址進行比較。如果兩者相同,都做出回覆,向對方報告自己的MAC地址,否則就丟棄這個包。


總之,有了ARP協議之後,我們就可以得到同一個子網路內的主機MAC地址,可以把資料包傳送到任意一臺主機之上了。

6、傳輸層


6.1傳輸層的由來


有了MAC地址和IP地址,我們已經可以在網際網路上任意兩臺主機上建立通訊。

接下來的問題是,同一臺主機上有許多程式都需要用到網路,比如,你一邊瀏覽網頁,一邊與朋友線上聊天。當一個資料包從網際網路上發來的時候,你怎麼知道,它是表示網頁的內容,還是表示線上聊天的內容?

也就是說,我們還需要一個引數,表示這個資料包到底供哪個程式(程式)使用。這個引數就叫做"埠"(port),它其實是每一個使用網路卡的程式的編號。每個資料包都發到主機的特定埠,所以不同的程式就能取到自己所需要的資料。

"埠"是0到65535之間的一個整數,正好16個二進位制位。0到1023的埠被系統佔用,使用者只能選用大於1023的埠。不管是瀏覽網頁還是線上聊天,應用程式會隨機選用一個埠,然後與伺服器的相應埠聯絡。

"傳輸層"的功能,就是建立"埠到埠"的通訊。相比之下,"網路層"的功能是建立"主機到主機"的通訊。只要確定主機和埠,我們就能實現程式之間的交流。因此,Unix系統就把主機+埠,叫做"套接字"(socket)。有了它,就可以進行網路應用程式開發了。

6.2UDP協議


現在,我們必須在資料包中加入埠資訊,這就需要新的協議。最簡單的實現叫做UDP協議,它的格式幾乎就是在資料前面,加上埠號。

UDP資料包,也是由"標頭"和"資料"兩部分組成:
 


"標頭"部分主要定義了發出埠和接收埠,"資料"部分就是具體的內容。然後,把整個UDP資料包放入IP資料包的"資料"部分,而前面說過,IP資料包又是放在乙太網資料包之中的,所以整個乙太網資料包現在變成了下面這樣:
 

UDP資料包非常簡單,"標頭"部分一共只有8個位元組,總長度不超過65,535位元組,正好放進一個IP資料包。

6.3TCP協議


UDP協議的優點是比較簡單,容易實現,但是缺點是可靠性較差,一旦資料包發出,無法知道對方是否收到。為了解決這個問題,提高網路可靠性,TCP協議就誕生了。這個協議非常複雜,但可以近似認為,它就是有確認機制的UDP協議,每發出一個資料包都要求確認。如果有一個資料包遺失,就收不到確認,發出方就知道有必要重發這個資料包了。

因此,TCP協議能夠確保資料不會遺失。它的缺點是過程複雜、實現困難、消耗較多的資源。

TCP資料包和UDP資料包一樣,都是內嵌在IP資料包的"資料"部分。TCP資料包沒有長度限制,理論上可以無限長,但是為了保證網路的效率,通常TCP資料包的長度不會超過IP資料包的長度,以確保單個TCP資料包不必再分割。

7、應用層


應用程式收到"傳輸層"的資料,接下來就要進行解讀。由於網際網路是開放架構,資料來源五花八門,必須事先規定好格式,否則根本無法解讀。"應用層"的作用,就是規定應用程式的資料格式。

舉例來說,TCP協議可以為各種各樣的程式傳遞資料,比如Email、WWW、FTP等等。那麼,必須有不同協議規定電子郵件、網頁、FTP資料的格式,這些應用程式協議就構成了"應用層"。這是最高的一層,直接面對使用者。它的資料就放在TCP資料包的"資料"部分。

因此,現在的乙太網的資料包就變成下面這樣:
 

8、一個小結

我們已經知道,網路通訊就是交換資料包。電腦A向電腦B傳送一個資料包,後者收到了,回覆一個資料包,從而實現兩臺電腦之間的通訊。

資料包的結構,基本上是下面這樣:
 

傳送這個包,需要知道兩個地址:

  • * 對方的MAC地址;

  • * 對方的IP地址。


有了這兩個地址,資料包才能準確送到接收者手中。但是,前面說過,MAC地址有侷限性,如果兩臺電腦不在同一個子網路,就無法知道對方的MAC地址,必須通過閘道器(gateway)轉發。

 

上圖中,1號電腦要向4號電腦傳送一個資料包。它先判斷4號電腦是否在同一個子網路,結果發現不是(後文介紹判斷方法),於是就把這個資料包發到閘道器A。閘道器A通過路由協議,發現4號電腦位於子網路B,又把資料包發給閘道器B,閘道器B再轉發到4號電腦。

1號電腦把資料包發到閘道器A,必須知道閘道器A的MAC地址。所以,資料包的目標地址,實際上分成兩種情況:

場景資料包地址
同一個子網路對方的MAC地址,對方的IP地址
非同一個子網路閘道器的MAC地址,對方的IP地址


傳送資料包之前,電腦必須判斷對方是否在同一個子網路,然後選擇相應的MAC地址。接下來,我們就來看,實際使用中,這個過程是怎麼完成的。

9、使用者的上網設定


9.1靜態IP地址


你買了一臺新電腦,插上網線,開機,這時電腦能夠上網嗎?

 

通常你必須做一些設定。有時,管理員(或者ISP)會告訴你下面四個引數,你把它們填入作業系統,計算機就能連上網了:

  • * 本機的IP地址;

  • * 子網掩碼;

  • * 閘道器的IP地址;

  • * DNS的IP地址。


下圖是Windows系統的設定視窗:
 

這四個引數缺一不可,後文會解釋為什麼需要知道它們才能上網。由於它們是給定的,計算機每次開機,都會分到同樣的IP地址,所以這種情況被稱作"靜態IP地址上網"。但是,這樣的設定很專業,普通使用者望而生畏,而且如果一臺電腦的IP地址保持不變,其他電腦就不能使用這個地址,不夠靈活。出於這兩個原因,大多數使用者使用"動態IP地址上網"。

9.2動態IP地址


所謂"動態IP地址",指計算機開機後,會自動分配到一個IP地址,不用人為設定。它使用的協議叫做DHCP協議。

這個協議規定,每一個子網路中,有一臺計算機負責管理本網路的所有IP地址,它叫做"DHCP伺服器"。新的計算機加入網路,必須向"DHCP伺服器"傳送一個"DHCP請求"資料包,申請IP地址和相關的網路引數。

前面說過,如果兩臺計算機在同一個子網路,必須知道對方的MAC地址和IP地址,才能傳送資料包。但是,新加入的計算機不知道這兩個地址,怎麼傳送資料包呢?DHCP協議做了一些巧妙的規定。

9.3DHCP協議


首先,它是一種應用層協議,建立在UDP協議之上,所以整個資料包是這樣的:
 

  • 1)最前面的"乙太網標頭":設定發出方(本機)的MAC地址和接收方(DHCP伺服器)的MAC地址。前者就是本機網路卡的MAC地址,後者這時不知道,就填入一個廣播地址:FF-FF-FF-FF-FF-FF。

  • 2)後面的"IP標頭":設定發出方的IP地址和接收方的IP地址。這時,對於這兩者,本機都不知道。於是,發出方的IP地址就設為0.0.0.0,接收方的IP地址設為255.255.255.255。

  • 3)最後的"UDP標頭":設定發出方的埠和接收方的埠。這一部分是DHCP協議規定好的,發出方是68埠,接收方是67埠。


這個資料包構造完成後,就可以發出了。乙太網是廣播傳送,同一個子網路的每臺計算機都收到了這個包。因為接收方的MAC地址是FF-FF-FF-FF-FF-FF,看不出是發給誰的,所以每臺收到這個包的計算機,還必須分析這個包的IP地址,才能確定是不是發給自己的。當看到發出方IP地址是0.0.0.0,接收方是255.255.255.255,於是DHCP伺服器知道"這個包是發給我的",而其他計算機就可以丟棄這個包。

接下來,DHCP伺服器讀出這個包的資料內容,分配好IP地址,傳送回去一個"DHCP響應"資料包。這個響應包的結構也是類似的,乙太網標頭的MAC地址是雙方的網路卡地址,IP標頭的IP地址是DHCP伺服器的IP地址(發出方)和255.255.255.255(接收方),UDP標頭的埠是67(發出方)和68(接收方),分配給請求端的IP地址和本網路的具體引數則包含在Data部分。

新加入的計算機收到這個響應包,於是就知道了自己的IP地址、子網掩碼、閘道器地址、DNS伺服器等等引數。

9.4上網設定:小結


這個部分,需要記住的就是一點:不管是"靜態IP地址"還是"動態IP地址",電腦上網的首要步驟,是確定四個引數。

這四個值很重要,值得重複一遍:

  • * 本機的IP地址;

  • * 子網掩碼;

  • * 閘道器的IP地址;

  • * DNS的IP地址。


有了這幾個數值,電腦就可以上網"衝浪"了。接下來,我們來看一個例項,當使用者訪問網頁的時候,網際網路協議是怎麼運作的。

10、一個例項:訪問網頁


10.1本機引數


我們假定,經過上一節的步驟,使用者設定好了自己的網路引數:

  • * 本機的IP地址:192.168.1.100;

  • * 子網掩碼:255.255.255.0;

  • * 閘道器的IP地址:192.168.1.1;

  • * DNS的IP地址:8.8.8.8。


然後他開啟瀏覽器,想要訪問Google,在位址列輸入了網址:www.google.com

 

這意味著,瀏覽器要向Google傳送一個網頁請求的資料包。

10.2DNS協議


我們知道,傳送資料包,必須要知道對方的IP地址。但是,現在,我們只知道網址www.google.com,不知道它的IP地址。DNS協議可以幫助我們,將這個網址轉換成IP地址。已知DNS伺服器為8.8.8.8,於是我們向這個地址傳送一個DNS資料包(53埠)。

 

然後,DNS伺服器做出響應,告訴我們Google的IP地址是172.194.72.105。於是,我們知道了對方的IP地址。

10.3子網掩碼


接下來,我們要判斷,這個IP地址是不是在同一個子網路,這就要用到子網掩碼。

已知子網掩碼是255.255.255.0,本機用它對自己的IP地址192.168.1.100,做一個二進位制的AND運算(兩個數位都為1,結果為1,否則為0),計算結果為192.168.1.0;然後對Google的IP地址172.194.72.105也做一個AND運算,計算結果為172.194.72.0。這兩個結果不相等,所以結論是,Google與本機不在同一個子網路。

因此,我們要向Google傳送資料包,必須通過閘道器192.168.1.1轉發,也就是說,接收方的MAC地址將是閘道器的MAC地址。

10.4應用層協議


瀏覽網頁用的是HTTP協議,它的整個資料包構造是這樣的:
 

HTTP部分的內容,類似於下面這樣:

1
2
3
4
5
6
7
8
9
GET / HTTP/1.1
Host: [url=http://www.google.com]www.google.com[/url]
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1) ......
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Cookie: ... ...


我們假定這個部分的長度為4960位元組,它會被嵌在TCP資料包之中。

10.5TCP協議


TCP資料包需要設定埠,接收方(Google)的HTTP埠預設是80,傳送方(本機)的埠是一個隨機生成的1024-65535之間的整數,假定為51775。TCP資料包的標頭長度為20位元組,加上嵌入HTTP的資料包,總長度變為4980位元組。

10.6IP協議


然後,TCP資料包再嵌入IP資料包。IP資料包需要設定雙方的IP地址,這是已知的,傳送方是192.168.1.100(本機),接收方是172.194.72.105(Google)。IP資料包的標頭長度為20位元組,加上嵌入的TCP資料包,總長度變為5000位元組。

10.7乙太網協議


最後,IP資料包嵌入乙太網資料包。乙太網資料包需要設定雙方的MAC地址,傳送方為本機的網路卡MAC地址,接收方為閘道器192.168.1.1的MAC地址(通過ARP協議得到)。

乙太網資料包的資料部分,最大長度為1500位元組,而現在的IP資料包長度為5000位元組。因此,IP資料包必須分割成四個包。因為每個包都有自己的IP標頭(20位元組),所以四個包的IP資料包的長度分別為1500、1500、1500、560。

 

10.8伺服器端響應


經過多個閘道器的轉發,Google的伺服器172.194.72.105,收到了這四個乙太網資料包。根據IP標頭的序號,Google將四個包拼起來,取出完整的TCP資料包,然後讀出裡面的"HTTP請求",接著做出"HTTP響應",再用TCP協議發回來。

本機收到HTTP響應以後,就可以將網頁顯示出來,完成一次網路通訊。

 

這個例子就到此為止,雖然經過了簡化,但它大致上反映了網際網路協議的整個通訊過程。

11、TCP 協議的作用


網際網路由一整套協議構成。TCP 只是其中的一層,有著自己的分工。

 
▲ TCP 是乙太網協議和 IP 協議的上層協議,也是應用層協議的下層協議

最底層的乙太網協議(Ethernet)規定了電子訊號如何組成資料包(packet),解決了子網內部的點對點通訊。

 
▲ 乙太網協議解決了區域網的點對點通訊

但是,乙太網協議不能解決多個區域網如何互通,這由 IP 協議解決。

 
▲ IP 協議可以連線多個區域網

IP 協議定義了一套自己的地址規則,稱為 IP 地址。它實現了路由功能,允許某個區域網的 A 主機,向另一個區域網的 B 主機傳送訊息。

 
▲ 路由器就是基於 IP 協議。區域網之間要靠路由器連線

路由的原理很簡單。市場上所有的路由器,背後都有很多網口,要接入多根網線。路由器內部有一張路由表,規定了 A 段 IP 地址走出口一,B 段地址走出口二,......通過這套"指路牌",實現了資料包的轉發。

 
▲ 本機的路由表註明了不同 IP 目的地的資料包,要傳送到哪一個網口(interface)

IP 協議只是一個地址協議,並不保證資料包的完整。如果路由器丟包(比如快取滿了,新進來的資料包就會丟失),就需要發現丟了哪一個包,以及如何重新傳送這個包。這就要依靠 TCP 協議。
簡單說,TCP 協議的作用是,保證資料通訊的完整性和可靠性,防止丟包。

12、TCP 資料包的大小


乙太網資料包(packet)的大小是固定的,最初是1518位元組,後來增加到1522位元組。其中, 1500 位元組是負載(payload),22位元組是頭資訊(head)。IP 資料包在乙太網資料包的負載裡面,它也有自己的頭資訊,最少需要20位元組,所以 IP 資料包的負載最多為1480位元組。

 
▲ IP 資料包在乙太網資料包裡面,TCP 資料包在 IP 資料包裡面

TCP 資料包在 IP 資料包的負載裡面。它的頭資訊最少也需要20位元組,因此 TCP 資料包的最大負載是 1480 - 20 = 1460 位元組。由於 IP 和 TCP 協議往往有額外的頭資訊,所以 TCP 負載實際為1400位元組左右。因此,一條1500位元組的資訊需要兩個 TCP 資料包。HTTP/2 協議的一大改進, 就是壓縮 HTTP 協議的頭資訊,使得一個 HTTP 請求可以放在一個 TCP 資料包裡面,而不是分成多個,這樣就提高了速度

▲ 乙太網資料包的負載是1500位元組,TCP 資料包的負載在1400位元組左右

13、TCP 資料包的編號(SEQ)


一個包1400位元組,那麼一次性傳送大量資料,就必須分成多個包。比如,一個 10MB 的檔案,需要傳送7100多個包。傳送的時候,TCP 協議為每個包編號(sequence number,簡稱 SEQ),以便接收的一方按照順序還原。萬一發生丟包,也可以知道丟失的是哪一個包。

第一個包的編號是一個隨機數。為了便於理解,這裡就把它稱為1號包。假定這個包的負載長度是100位元組,那麼可以推算出下一個包的編號應該是101。這就是說,每個資料包都可以得到兩個編號:自身的編號,以及下一個包的編號。接收方由此知道,應該按照什麼順序將它們還原成原始檔案。

▲ 當前包的編號是45943,下一個資料包的編號是46183,由此可知,這個包的負載是240位元組

14、TCP 資料包的組裝


收到 TCP 資料包以後,組裝還原是作業系統完成的。應用程式不會直接處理 TCP 資料包。對於應用程式來說,不用關心資料通訊的細節。除非線路異常,收到的總是完整的資料。應用程式需要的資料放在 TCP 資料包裡面,有自己的格式(比如 HTTP 協議)。

TCP 並沒有提供任何機制,表示原始檔案的大小,這由應用層的協議來規定。比如,HTTP 協議就有一個頭資訊Content-Length,表示資訊體的大小。對於作業系統來說,就是持續地接收 TCP 資料包,將它們按照順序組裝好,一個包都不少。

作業系統不會去處理 TCP 資料包裡面的資料。一旦組裝好 TCP 資料包,就把它們轉交給應用程式。TCP 資料包裡面有一個埠(port)引數,就是用來指定轉交給監聽該埠的應用程式。



 

系統根據 TCP 資料包裡面的埠,將組裝好的資料轉交給相應的應用程式。上圖中,21埠是 FTP 伺服器,25埠是 SMTP 服務,80埠是 Web 伺服器。

應用程式收到組裝好的原始資料,以瀏覽器為例,就會根據 HTTP 協議的 Content-Length 欄位正確讀出一段段的資料。這也意味著,一次 TCP 通訊可以包括多個 HTTP 通訊。

15、慢啟動和 ACK


伺服器傳送資料包,當然越快越好,最好一次性全發出去。但是,發得太快,就有可能丟包。頻寬小、路由器過熱、快取溢位等許多因素都會導致丟包。線路不好的話,發得越快,丟得越多。

最理想的狀態是,線上路允許的情況下,達到最高速率。但是我們怎麼知道,對方線路的理想速率是多少呢?答案就是慢慢試。

TCP 協議為了做到效率與可靠性的統一,設計了一個慢啟動(slow start)機制。開始的時候,傳送得較慢,然後根據丟包的情況,調整速率:如果不丟包,就加快傳送速度;如果丟包,就降低傳送速度。

Linux 核心裡面設定了(常量TCP_INIT_CWND),剛開始通訊的時候,傳送方一次性傳送10個資料包,即"傳送視窗"的大小為10。然後停下來,等待接收方的確認,再繼續傳送。預設情況下,接收方每收到兩個 TCP 資料包,就要傳送一個確認訊息。"確認"的英語是 acknowledgement,所以這個確認訊息就簡稱 ACK。

ACK 攜帶兩個資訊:

  • 1)期待要收到下一個資料包的編號;

  • 2)接收方的接收視窗的剩餘容量。


傳送方有了這兩個資訊,再加上自己已經發出的資料包的最新編號,就會推測出接收方大概的接收速度,從而降低或增加傳送速率。這被稱為"傳送視窗",這個視窗的大小是可變的。

▲ 每個 ACK 都帶有下一個資料包的編號,以及接收視窗的剩餘容量,雙方都會傳送 ACK

注意:由於 TCP 通訊是雙向的,所以雙方都需要傳送 ACK。兩方的視窗大小,很可能是不一樣的。而且 ACK 只是很簡單的幾個欄位,通常與資料合併在一個資料包裡面傳送。

上圖一共4次通訊。第一次通訊,A 主機發給B 主機的資料包編號是1,長度是100位元組,因此第二次通訊 B 主機的 ACK 編號是 1 + 100 = 101,第三次通訊 A 主機的資料包編號也是 101。同理,第二次通訊 B 主機發給 A 主機的資料包編號是1,長度是200位元組,因此第三次通訊 A 主機的 ACK 是201,第四次通訊 B 主機的資料包編號也是201。

即使對於頻寬很大、線路很好的連線,TCP 也總是從10個資料包開始慢慢試,過了一段時間以後,才達到最高的傳輸速率。這就是 TCP 的慢啟動。

16、資料包的遺失處理


TCP 協議可以保證資料通訊的完整性,這是怎麼做到的?

前面說過,每一個資料包都帶有下一個資料包的編號。如果下一個資料包沒有收到,那麼 ACK 的編號就不會發生變化。

舉例來說,現在收到了4號包,但是沒有收到5號包。ACK 就會記錄,期待收到5號包。過了一段時間,5號包收到了,那麼下一輪 ACK 會更新編號。如果5號包還是沒收到,但是收到了6號包或7號包,那麼 ACK 裡面的編號不會變化,總是顯示5號包。這會導致大量重複內容的 ACK。

如果傳送方發現收到三個連續的重複 ACK,或者超時了還沒有收到任何 ACK,就會確認丟包,即5號包遺失了,從而再次傳送這個包。通過這種機制,TCP 保證了不會有資料包丟失。

▲ Host B 沒有收到100號資料包,會連續發出相同的 ACK,觸發 Host A 重發100號資料包

17、建立連線方式的差異


17.1TCP


說到 TCP 建立連線,相信大多數人腦海裡肯定可以浮現出一個詞,沒錯就是--“三次握手”。TCP 通過“三次握手”來建立連線,再通過“四次揮手”斷開一個連線。在每次揮手中 TCP 做了哪些操作呢?

流程如下圖所示(TCP的三次握手和四次揮手):

上圖就從客戶端和服務端的角度,清楚的展示了 TCP 的三次握手和四次揮手。可以看到,當 TCP 試圖建立連線時,三次握手指的是客戶端主動觸發了兩次,服務端觸發了一次。

我們可以先明確一下 TCP 建立連線並且初始化的目標是什麼呢?

  • 1)初始化資源;

  • 2)告訴對方我的序列號。


所以三次握手的次序是這樣子的:

  • 1)client端首先傳送一個SYN包告訴Server端我的初始序列號是X;

  • 2)Server端收到SYN包後回覆給client一個ACK確認包,告訴client說我收到了;

  • 3)接著Server端也需要告訴client端自己的初始序列號,於是Server也傳送一個SYN包告訴client我的初始序列號是Y;

  • 4)Client收到後,回覆Server一個ACK確認包說我知道了。


其中的 2 、3 步驟可以簡化為一步,也就是說將 ACK 確認包和 SYN 序列化包一同傳送給 Client 端。到此我們就比較簡單的解釋了 TCP 建立連線的“三次握手”。

17.2UDP


我們都知道 TCP 是面向連線的、可靠的、有序的傳輸層協議,而 UDP 是面向資料包的、不可靠的、無序的傳輸協議,所以 UDP 壓根不會建立什麼連線。

就好比發簡訊一樣,UDP 只需要知道對方的 ip 地址,將資料包一份一份的傳送過去就可以了,其他的作為傳送方,都不需要關心。

18、資料傳送方式的差異


關於 TCP、UDP 之間資料傳送的差異,可以體現二者最大的不同之處:

  • TCP:
    由於 TCP 是建立在兩端連線之上的協議,所以理論上傳送的資料流不存在大小的限制。但是由於緩衝區有大小限制,所以你如果用 TCP 傳送一段很大的資料,可能會截斷成好幾段,接收方依次的接收。

  • UDP:
    由於 UDP 本身傳送的就是一份一份的資料包,所以自然而然的就有一個上限的大小。


那麼每次 UDP 傳送的資料包大小由哪些因素共同決定呢?

  • UDP協議本身,UDP協議中有16位的UDP報文長度,那麼UDP報文長度不能超過2^16=65536;

  • 乙太網(Ethernet)資料幀的長度,資料鏈路層的MTU(最大傳輸單元);

  • socket的UDP傳送快取區大小。


先來看第一個因素,UDP 本身協議的報文長度為 2^16 - 1,UDP 包頭佔 8 個位元組,IP 協議本身封裝後包頭佔 20 個位元組,所以最終長度為: 2^16 - 1 - 20 - 8 = 65507 位元組。

只看第一個因素有點理想化了,因為 UDP 屬於不可靠協議,我們應該儘量避免在傳輸過程中,資料包被分割。所以這裡有一個非常重要的概念 MTU -- 也就是最大傳輸單元。

在 Internet 下 MTU 的值為 576 位元組,所以在 internet 下使用 UDP 協議,每個資料包最大的位元組數為: 576 - 20 - 8 = 548

19、資料有序性的差異


我們再來談談資料的有序性。

19.1TCP


對於 TCP 來說,本身 TCP 有著超時重傳、錯誤重傳、還有等等一系列複雜的演算法保證了 TCP 的資料是有序的,假設你傳送了資料 1、2、3,則只要傳送端和接收端保持連線時,接收端收到的資料始終都是 1、2、3。

19.2UDP


而 UDP 協議則要奔放的多,無論 server 端無論緩衝池的大小有多大,接收 client 端發來的訊息總是一個一個的接收。並且由於 UDP 本身的不可靠性以及無序性,如果 client 傳送了 1、2、3 這三個資料包過來,server 端接收到的可能是任意順序、任意個數三個資料包的排列組合。

20、可靠性的差異


其實大家都知道 TCP 本身是可靠的協議,而 UDP 是不可靠的協議。

20.1TCP


TCP 內部的很多演算法機制讓他保持連線的過程中是很可靠的。比如:TCP 的超時重傳、錯誤重傳、TCP 的流量控制、阻塞控制、慢熱啟動演算法、擁塞避免演算法、快速恢復演算法 等等。所以 TCP 是一個內部原理複雜,但是使用起來比較簡單的這麼一個協議。

20.2UDP


UDP 是一個面向非連線的協議,UDP 傳送的每個資料包帶有自己的 IP 地址和接收方的 IP 地址,它本身對這個資料包是否出錯,是否到達不關心,只要發出去了就好了。

所以來研究下,什麼情況會導致 UDP 丟包:

  • 資料包分片重組丟失:在文章之前我們就說過,UDP 的每個資料包大小多少最合適,事實上 UDP 協議本身規定的大小是 64kb,但是在資料鏈路層有 MTU 的限制,大小大概在 5kb,所以當你傳送一個很大的 UDP 包的時候,這個包會在 IP 層進行分片,然後重組。這個過程就有可能導致分片的包丟失。UDP 本身有 CRC 檢測機制,會拋棄掉丟失的 UDP 包;

  • UDP 緩衝區填滿:當 UDP 的緩衝區已經被填滿的時候,接收方還沒有處理這部分的 UDP 資料包,這個時候再過來的資料包就沒有地方可以存了,自然就都被丟棄了。


21、使用場景總結


聊聊 TCP、UDP 使用場景。

先來說 UDP 的吧,有很多人都會覺得 UDP 與 TCP 相比,在效能速度上是佔優勢的。因為 UDP 並不用保持一個持續的連線,也不需要對收發包進行確認。但事實上經過這麼多年的發展 TCP 已經擁有足夠多的演算法和優化,在網路狀態不錯的情況下,TCP 的整體效能是優於 UDP 的。

那在什麼時候我們非用 UDP 不可呢?

  • 對實時性要求高:比如實時會議,實時視訊這種情況下,如果使用 TCP,當網路不好發生重傳時,畫面肯定會有延時,甚至越堆越多。如果使用 UDP 的話,即使偶爾丟了幾個包,但是也不會影響什麼,這種情況下使用 UDP 比較好;

  • 多點通訊:TCP 需要保持一個長連線,那麼在涉及多點通訊的時候,肯定需要和多個通訊節點建立其雙向連線,然後有時在NAT環境下,兩個通訊節點建立其直接的 TCP 連線不是一個容易的事情,而 UDP 可以無需保持連線,直接發就可以了,所以成本會很低,而且穿透性好。這種情況下使用 UDP 也是沒錯的。


以上我們說了 UDP 的使用場景,在此之外的其他情況,使用 TCP 準沒錯。

22、網速的提升給UDP穩定性提供可靠網路保障


CDN服務商Akamai報告從2008年到2015年7年時間,各個國家網路平均速率由1.5Mbps提升為5.1Mbps,網速提升近4倍。網路環境變好,網路傳輸的延遲、穩定性也隨之改善,UDP的丟包率低於5%,如果再使用應用層重傳,能夠完全確保傳輸的可靠性。

23、對比測試結果UDP效能優於TCP


為了提升瀏覽速度,Google基於TCP提出了SPDY協議以及HTTP/2。Google在Chrome上實驗基於UDP的QUIC協議,傳輸速率減少到100ms以內。

  • Google採用QUIC後連線速率能有效提升75%;

  • Google搜尋採用QUIC後頁面載入效能提升3%;

  • YouTube採用QUIC後重新緩衝次數減少了30%。


24、TCP設計過於冗餘,速度難以進一步提升


TCP為了實現網路通訊的可靠性,使用了複雜的擁塞控制演算法,建立了繁瑣的握手過程以及重傳策略。由於TCP內建在系統協議棧中,極難對其進行改進。 

25、UDP協議以其簡單、傳輸快的優勢,在越來越多場景下取代了TCP


25.1網頁瀏覽


使用UDP協議有三個優點 :

  • 能夠對握手過程進行精簡,減少網路通訊往返次數;

  • 能夠對TLS加解密過程進行優化;

  • 收發快速,無阻塞。


25.2流媒體


採用TCP,一旦發生丟包,TCP會將後續包快取起來,等前面的包重傳並接收到後再繼續傳送,延遲會越來越大。基於UDP的協議如實時音視訊開源工程WebRTC是極佳的選擇。

2010年google 通過收購 Global IP Solutions,獲得了WebRTC(網頁實時通訊Web Real-Time Communication)技術,用於提升網頁視訊速率。

25.3實時遊戲


對實時要求較為嚴格的情況下,採用自定義的可靠UDP協議,比如Enet、RakNet(使用者有 sony online game、minecraft)等,自定義重傳策略,能夠把丟包產生的延遲降到最低,儘量減少網路問題對遊戲性造成的影響。

採用UDP的經典遊戲如FPS遊戲Quake、CS,著名的遊戲引擎Unity3D採用的也是RakNet。

25.4物聯網


2014年google旗下的Nest建立Thread Group,推出了物聯網通訊協議Thread,完善物聯網通訊。

採用UDP有3個關鍵點:

  • 網路頻寬需求較小,而實時性要求高;

  • 大部分應用無需維持連線;

  • 需要低功耗。


26、本文小結


如今全球將近50%的人都在使用網際網路,人們不斷的追求更快、更好的服務,一切都在變化,在越來越多的領域,UDP將會搶佔TCP的主導地位。


能認真看到這裡的都是真愛

公眾號推薦:

相關文章