­

自頂向下 | 帶你遨遊運輸層

  • 2020 年 3 月 18 日
  • 筆記

前言

本文已經收錄到我的Github個人部落格,歡迎大佬們光臨寒舍:

我的GIthub部落格

學習導圖:

學習導圖

一.運輸層概述

  • 運輸層為運行在不同主機上的應用程式之間提供邏輯通訊
  • 應用報文加上運輸層首部形成運輸層報文段,報文段通過網路層被封裝成網路層分組(數據報)向目的地發送

Q1:運輸層和網路層的關係

  • 運輸層:運行在不同主機上的應用程式之間提供邏輯通訊
  • 網路層:提供主機之間的通訊

舉個例子來說明兩者關係:

有兩個家庭,一家位於廣州,一家位於北京,每家有 3個孩子。這兩個家庭的孩子們喜歡彼此通訊,每封信都用單獨的信封通過傳統的郵政服務發送。每個家庭有一個孩子負責收發郵件,北京家庭是 阿京,而廣州家庭是 阿州。每周阿京去她所有的兄弟姐妹那裡收集郵件,並將這些郵件交到郵遞員處上。當信件到達北京家庭時,阿京也負責將信件發到她的兄弟姐妹手上,廣州家庭中 阿州也負責類似工作

  • 網路層——郵遞員
  • 運輸層——阿京和阿州
  • 應用程式——兄弟姐妹
  • 主機——兩個家庭

通過運輸層協議,兩台電腦彷彿直接相連一樣。應用無需知道底層內部實現的原理和細節,比如怎麼把遠隔世界兩地電腦上的數據進行相互傳輸

Q2:注意點

  • 運輸層協議僅僅實現在網路的邊緣處,例如主機,電腦,手機等。如路由器,交換機這些網路核心設備,是沒有實現運輸層協議的
  • 每一層協議僅僅檢查分組相應的協議層欄位
  • 最常用的兩種輸入層協議,TCPUDP
  • 運輸層的下面是網路層,網路層的目的在於為不同的主機提供邏輯通訊,而非主機上的進程
  • 網路層常用協議是 IP,其提供的是一種不可靠的數據傳輸服務
  • 為了做到為不同主機 (host) 上的應用或者說進程提供邏輯通訊這一目的,運輸層協議必須能分辨出數據的來源和去向。為此,運輸層存在著兩種行為,多路復用和多路分解

二.多路復用與多路分解

  • 多路復用:當運輸層收到來自上方應用層的數據時,運輸層會為數據封上一些頭部資訊,根據所有協議不同,封上的資訊也不一樣

用上面的兩個家庭的例子進行形象化地闡述就是:多路復用就是阿州和阿京將兄弟姐妹的信一起交給郵遞員

  • 多路分解:當運輸層收到下方網路層傳輸來的數據時,運輸層會檢查多路復用時封上的資訊,從而正確的把數據定向到相應的進程

Q1:如何使用運輸層的協議?

作業系統提供了被稱為 socket 的介面 api 供編程人員調用,對 socket 的形象理解是其是一種抽象,將複雜的實現 (tcp/udp) 協議的各種行為抽形成簡單的幾個函數給開發人員使用。就像瀏覽器將發送請求報文這一 http 協議規定的行為,抽象成我們只需要輸入 url 然後回車即可

這裡需要注意的一點是:

  • 在一般情況下,一個電腦埠只能被一個進程佔用
  • 一個進程可以創建多個 Socket,多個 TCP Socket 可以監聽同一個埠,並保證接受的數據依舊是正確的
  • 多個 UDP Socket 就無法監聽同一埠
  • 這其中的差異源於 TCPUDP 協議的不同

    • TCP 是面向連接的,其有足夠狀態的資訊來分辨數據來源,後定向到正確的 socket
    • UDP 不需要維持連接,僅僅通過埠號來決定數據的去向,所以會導致衝突

三.UDPTCP的多路復用和分解

Q1:UDP的多路復用和分解

一個 UDP Socket 通過一個二元組 (目的 IP 地址,目的埠號) 來標識,當輸入層收到數據時,通過檢查這個二元組,來定向數據該去往哪一個 UDP Socket。這也是多個 UDP Socket 無法監聽同一個埠的原因

Q2:TCP 的多路復用分解

一個 TCP Socket 通過一個四元組 (源 IP,源埠,目的 IP,目的埠號) 來標識,這也解釋為什麼多個 TCP Socket 可以監聽同一個埠,儘管目的 IP和目的埠號是一樣的,但是源 IP和源埠的組合總是不同的

TCP UDP Socket對比

四.UDP

4.1 UDP基本概念

相比於 TCP來講,UDP是一個簡單的協議,就是把網路層 IP 提供的服務封裝了下,實現了多路復用和分解,提供了端到端進程間的通訊和錯誤檢驗服務

相對於 TCP 來說:

缺點:

  • UDP 是不可靠的傳輸服務
  • 沒有流量和擁塞控制

優點:

  • 能夠夠精細的控制數據的發送時間和速率
  • 無需事先建立連接
  • 無連接狀態
  • 分組首部開銷小

UDP報文端結構:

UDP 報文段結構

  • 源埠號:發送方的埠號
  • 目的埠號:接收方埠號
  • 長度:包括首部在內的報文長度
  • 檢驗和:用來差錯檢驗。只發現錯誤不糾正,錯了就扔。然後重發

4.2 可靠數據傳輸

Q1:數據傳輸可能遇到的問題:

  • 傳輸中數據被損壞
  • 數據丟失
  • 數據可能亂序到達

Q2:解決方法:

  • 檢驗和
  • 序號
  • 定時器
  • 肯定和否定回饋分組

Q3:如何在保證可靠性的前提下,提高其性能?

  • 通過引入流水線 (pipelining) 技術

引入流水線導致了:

  • 序號範圍需要增加
  • 收發雙方可能需要快取亂序到達的分組
  • 以上兩個的具體實現取決於傳輸協議如何處理分組丟失、損壞的問題 (是選擇回退 N 步,還是選擇重傳)

Q4:如何處理分組丟失、損壞的問題

A.回退 N

  • 其核心在於,發送方會維持一個窗口,發送方能發送的數據量取決於窗口長度,並且當丟失時會重送所有未確認的分組
  • 接收方會丟棄亂序到達的快取

  • 特點:

    1.累計ACK

    2.單一定時器

B.選擇重傳

  • 核心在於,收發雙方都會維持一個窗口,並且儘力保證窗口的狀態是同步的,因此當分包丟失時,發送方只會重送丟失的分組
  • 接收方會快取亂序到達的分組

  • 特點:

    1.獨立ACK

    2.多個定時器

五.TCP

5.1 TCP基本概念

A.特點:

  • 面向連接
  • 全雙工
  • 點對點,不存在一次發送將數據傳遞給多個接收方、
  • 在合適的時候發送 發送快取 里的數據
  • 為每個數據封上一個 TCP 頭部
  • TCP 連接的每一端都具有發送快取和接受快取

B.報文段結構

TCP報文段結構

部分參數解釋:

  • 序號 (seq) :所帶數據的第一個比特的序號,同時也是接收方期待的序號,等於接收方回復報文中的 ACK(n) 中的 n

  • 確認號 (ack) : 對於一個 ACK(n) 來說,告訴對方 n-1 前的數據已經收到,下一次期待的序列號為 n

  • ACK :指示,用於指示報文中確認號欄位的值是有效的

  • PSH :指示,立即發送_發送快取_里的數據

  • RST :指示,用於強制關閉連接

  • SYN : 指示,用於握手階段也就是建立連接的階段

  • FIN :指示,用於正常關閉連接

  • 接受窗口 :用於 TCP 的流量控制功能

5.2 可靠數據傳輸

  • TCP 協議為數據的每一 Byte 都編號,而非針對報文段

  • 總是維持最老未經確認的 1 Byte 的計時器

  • 每一次超時重置計時器時間會加倍

  • 錯誤恢復機制回退 N 步和選擇重傳的混合體

  • 不會丟棄亂序到達的分組,而是快取起來
  • 採用累計性 ACK
  • 只會重傳丟失報文段中的數據
  • 快速重傳:當連續收到 4 個相同的 ACK 時 (一個正常的 ACK,三個正常 ACK 的重複),會觸發快速重傳,立即重發分組

5.3 流量控制

  • 為了防止過高數據流量導致接收者的接受快取爆掉,接收者會在其 TCP報文中通過 接受窗口 指示發收者還能發送多少數據

接受窗口 (rwnd) 公式:

  • rwnd = RcvBuffer - [LastByteRead - LastbyteRead]
  • 且:LastByteSent - LastByteAcked <= rwnd

5.4 TCP 連接管理

Q1:建立連接(三次握手)

三次握手

  1. 客戶端發送 SYN 位置 1 的報文段
  2. 服務端返回 SYN 為 1,ACK 為 1 的報文段
  3. 客戶端發送 ACK 為 1,且附帶數據的報文段

形象化地理解:

TCP 三次握手就好比兩個人在街上隔著50米看見了對方,但是因為霧霾等原因不能100%確認,所以要通過招手的方式相互確定對方是否認識自己。

張三首先向李四招手(syn),李四看到張三向自己招手後,向對方點了點頭擠出了一個微笑(ack)。張三看到李四微笑後確認了李四成功辨認出了自己(進入estalished狀態)

但是李四還有點狐疑,向四周看了一看,有沒有可能張三是在看別人呢,他也需要確認一下。所以李四也向張三招了招手(syn),張三看到李四向自己招手後知道對方是在尋求自己的確認,於是也點了點頭擠出了微笑(ack),李四看到對方的微笑後確認了張三就是在向自己打招呼(進入established狀態)。

於是兩人加快步伐,走到了一起,相互擁抱

張三李四

Q2:斷開連接(四次揮手)

四次揮手

  1. 客戶發送 FIN 為 1 的報文段
  2. 服務端返回 ACK 為 1 的報文段
  3. 服務端發送 FIN 為 1 的報文段
  4. 客戶端返回 ACK 為 1 的報文段
  5. 客戶端在一段時間後,關閉連接

形象化地理解:

張三揮手(fin)——李四傷感地微笑(ack)——李四揮手(fin)——張三傷感地微笑

六.擁塞控制

Q1:擁塞的代價

  • 導致分組過長的排隊時延
  • 需要重傳快取溢出丟失的分組
  • 高延時導致重送分組
  • 丟包導致運輸相關分組的分組交換器所作的工作全部白費

Q2:TCP 的擁塞控制

  • TCP 採用端到端的擁塞控制

  • 三個主要問題:

  1. 一個 TCP 的發送方如何限制自己的發送流量的速率?

通過設置一個擁塞窗口 (cwnd), 並且保證:LastByteSent - LastByteAcked <= min{cwnd, rwnd}

  1. 如何感知其發送路徑擁塞了?
  • timeout
  • 收到一次正常 ACK 後連續收到三次正常 ACK 的重複
  1. 感到擁塞時,採用什麼樣的演算法改變發送速率?
  • 慢啟動

cwnd 的值從 1 MSS 開始,並且對每一個 ACKcwnd 值變為原來的 2 倍,直到超過閾值 (ssthresh),轉為擁塞避免模式

  • 擁塞避免

在每一個 RRT 時間,cwnd 的值增加一個 MSS

  • 快速恢復

cwnd 的值降為一半加上重複收到的重複 ACK 的數量,並且每一個 ACKcwnd 的值增加一個 MSS

在實踐中,一旦 timeout 就會會到慢啟動的狀態,多次重複 ACK 則會進入快速恢復狀態

Q3:TCP 公平

TCP 的公平性在於保證每個連接的吞吐量是平均的,而不是應用或進程間

七.再談握手和揮手

7.1 為啥一定要三次握手,兩次不行嗎?

弄清這個問題,我們需要先弄明白三次握手的目的是什麼,能不能只用兩次握手來達到同樣的目的。

  • 第一次握手:客戶端發送網路包,服務端收到了。 這樣服務端就能得出結論:客戶端的發送能力、服務端的接收能力是正常的。
  • 第二次握手:服務端發包,客戶端收到了。 這樣客戶端就能得出結論:服務端的接收、發送能力,客戶端的接收、發送能力是正常的。不過此時伺服器並不能確認客戶端的接收能力是否正常。
  • 第三次握手:客戶端發包,服務端收到了。 這樣服務端就能得出結論:客戶端的接收、發送能力正常,伺服器自己的發送、接收能力也正常。

因此,需要三次握手才能確認雙方的接收與發送能力是否正常。

試想如果是用兩次握手,可能會出現下面這種情況:

如客戶端發出連接請求,但因連接請求報文丟失而未收到確認,於是客戶端再重傳一次連接請求。後來收到了確認,建立了連接。數據傳輸完畢後,就釋放了連接,客戶端共發出了兩個連接請求報文段,其中第一個丟失,第二個到達了服務端,但是第一個丟失的報文段只是在某些網路結點長時間滯留了,延誤到連接釋放以後的某個時間才到達服務端,此時服務端誤認為客戶端又發出一次新的連接請求,於是就向客戶端發出確認報文段,同意建立連接,不採用三次握手,只要服務端發出確認,就建立新的連接了,此時客戶端忽略服務端發來的確認,也不發送數據,則服務端一致等待客戶端發送數據,浪費資源

7.2 為啥揮手要四次?

這是因為服務端在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACKSYN放在一個報文里發送給客戶端。而關閉連接時,當收到對方的FIN報文時,僅僅表示對方不再發送數據了但是還能接收數據,所以服務端可以立即close,也可以發送一些數據給客戶端後,再發送FIN報文給客戶端來表示同意現在關閉連接,因此,服務端ACKFIN一般都會分開發送。


如果文章對您有一點幫助的話,希望您能點一下贊,您的點贊,是我前進的動力

本文參考鏈接: