8000+字總結:一文搞定 UDP 和 TCP 高頻面試題!
- 2020 年 3 月 4 日
- 筆記
來源:herongwei
作者:herongwei
找工作面試,經常會被問到 UDP 和 TCP,今天給大家總結其中的核心高頻面試題,再有面試官問你相關的知識點,看這篇就夠了!
PS:文章有點長,請耐心閱讀。
目錄:
1、UDP 和 TCP 的特點與區別
2、UDP 、TCP 首部格式
3、TCP 的三次握手和四次揮手
4、TCP 的三次握手(為什麼三次?)
5、TCP 的四次揮手(為什麼四次?)
6、TCP 長連接和短連接的區別
7、TCP粘包、拆包及解決辦法
8、TCP 可靠傳輸
9、TCP 滑動窗口
10、TCP 流量控制
11、TCP 擁塞控制
12、提供網路利用率
前言
網路層只把分組發送到目的主機,但是真正通訊的並不是主機而是主機中的進程。傳輸層提供了進程間的邏輯通訊,傳輸層向高層用戶屏蔽了下面網路層的核心細節,使應用程式看起來像是在兩個傳輸層實體之間有一條端到端的邏輯通訊信道。
1、UDP 和 TCP 的特點與區別
用戶數據報協議 UDP(User Datagram Protocol)
是無連接的,盡最大可能交付,沒有擁塞控制,面向報文(對於應用程式傳下來的報文不合併也不拆分,只是添加 UDP 首部),支援一對一、一對多、多對一和多對多的交互通訊。
傳輸控制協議 TCP(Transmission Control Protocol)
是面向連接的,提供可靠交付,有流量控制,擁塞控制,提供全雙工通訊,面向位元組流(把應用層傳下來的報文看成位元組流,把位元組流組織成大小不等的數據塊),每一條 TCP 連接只能是點對點的(一對一)。
2、UDP 、TCP 首部格式
UDP 首部欄位只有 8 個位元組,包括源埠、目的埠、長度、檢驗和。12 位元組的偽首部是為了計算檢驗和臨時添加的。
TCP 首部格式比 UDP 複雜。
序號:用於對位元組流進行編號,例如序號為 301,表示第一個位元組的編號為 301,如果攜帶的數據長度為 100 位元組,那麼下一個報文段的序號應為 401。
確認號:期望收到的下一個報文段的序號。例如 B 正確收到 A 發送來的一個報文段,序號為 501,攜帶的數據長度為 200 位元組,因此 B 期望下一個報文段的序號為 701,B 發送給 A 的確認報文段中確認號就為 701。
數據偏移:指的是數據部分距離報文段起始處的偏移量,實際上指的是首部的長度。
控制位:八位從左到右分別是 CWR,ECE,URG,ACK,PSH,RST,SYN,FIN。
CWR:CWR 標誌與後面的 ECE 標誌都用於 IP 首部的 ECN 欄位,ECE 標誌為 1 時,則通知對方已將擁塞窗口縮小;
ECE:若其值為 1 則會通知對方,從對方到這邊的網路有阻塞。在收到數據包的 IP 首部中 ECN 為 1 時將 TCP 首部中的 ECE 設為 1;
URG:該位設為 1,表示包中有需要緊急處理的數據,對於需要緊急處理的數據,與後面的緊急指針有關;
ACK:該位設為 1,確認應答的欄位有效,TCP規定除了最初建立連接時的 SYN 包之外該位必須設為 1;
PSH:該位設為 1,表示需要將收到的數據立刻傳給上層應用協議,若設為 0,則先將數據進行快取;
RST:該位設為 1,表示 TCP 連接出現異常必須強制斷開連接;
SYN:用於建立連接,該位設為 1,表示希望建立連接,並在其序列號的欄位進行序列號初值設定;
FIN:該位設為 1,表示今後不再有數據發送,希望斷開連接。當通訊結束希望斷開連接時,通訊雙方的主機之間就可以相互交換 FIN 位置為 1 的 TCP 段。
每個主機又對對方的 FIN 包進行確認應答之後可以斷開連接。不過,主機收到 FIN 設置為 1 的 TCP 段之後不必馬上回復一個 FIN 包,而是可以等到緩衝區中的所有數據都因為已成功發送而被自動刪除之後再發 FIN 包;
窗口:窗口值作為接收方讓發送方設置其發送窗口的依據。之所以要有這個限制,是因為接收方的數據快取空間是有限的。
3、什麼是 TCP 的三次握手和四次揮手?
TCP 是一種面向連接的單播協議,在發送數據前,通訊雙方必須在彼此間建立一條連接。所謂的「連接」,其實是客戶端和伺服器的記憶體里保存的一份關於對方的資訊,如 IP 地址、埠號等。
TCP 可以看成是一種位元組流,它會處理 IP 層或以下的層的丟包、重複以及錯誤問題。在連接的建立過程中,雙方需要交換一些連接的參數。這些參數可以放在 TCP 頭部。
TCP 提供了一種可靠、面向連接、位元組流、傳輸層的服務,採用三次握手建立一個連接;採用四次揮手來關閉一個連接。
一個 TCP 連接由一個 4 元組構成,分別是兩個 IP 地址和兩個埠號。一個TCP連接通常分為三個階段:啟動、數據傳輸、退出(關閉)。
當 TCP 接收到另一端的數據時,它會發送一個確認,但這個確認不會立即發送,一般會延遲一會(提供網路利用率這部分有講到)。
ACK 是累積的,一個確認位元組號 N 的 ACK 表示所有直到 N 的位元組(不包括 N)已經成功被接收了。這樣的好處是如果一個 ACK 丟失,很可能後續的 ACK 就足以確認前面的報文段了。
一個完整的 TCP 連接是雙向和對稱的,數據可以在兩個方向上平等地流動。給上層應用程式提供一種雙工服務。一旦建立了一個連接,這個連接的一個方向上的每個 TCP 報文段都包含了相反方向上的報文段的一個 ACK。
序列號的作用是使得一個 TCP 接收端可丟棄重複的報文段,記錄以雜亂次序到達的報文段。因為 TCP 使用 IP 來傳輸報文段,而IP 不提供重複消除或者保證次序正確的功能。
另一方面,TCP 是一個位元組流協議,絕不會以雜亂的次序給上層程式發送數據。因此 TCP 接收端會被迫先保持大序列號的數據不交給應用程式,直到缺失的小序列號的報文段被填滿。
4、TCP 的三次握手(為什麼三次?)
三次握手:
假設 A 為客戶端,B 為伺服器端。
首先 B 處於 LISTEN(監聽)狀態,等待客戶的連接請求。
- A 向 B 發送連接請求報文,SYN=1,ACK=0,選擇一個初始的序號 x。
- B 收到連接請求報文,如果同意建立連接,則向 A 發送連接確認報文,SYN=1,ACK=1,確認號為 x+1,同時也選擇一個初始的序號 y。
- A 收到 B 的連接確認報文後,還要向 B 發出確認,確認號為 y+1,序號為 x+1。
B 收到 A 的確認後,連接建立。
為什麼三次?
1、第三次握手是為了防止失效的連接請求到達伺服器,讓伺服器錯誤打開連接。
2、換個易於理解的視角來看為什麼要 3 次握手。
客戶端和服務端通訊前要進行連接,「3次握手」的作用就是雙方都能明確自己和對方的收、發能力是正常的。
第一次握手:客戶端發送網路包,服務端收到了。這樣服務端就能得出結論:客戶端的發送能力、服務端的接收能力是正常的。
第二次握手:服務端發包,客戶端收到了。這樣客戶端就能得出結論:服務端的接收、發送能力,客戶端的接收、發送能力是正常的。從客戶端的視角來看,我接到了服務端發送過來的響應數據包,說明服務端接收到了我在第一次握手時發送的網路包,並且成功發送了響應數據包,這就說明,服務端的接收、發送能力正常。而另一方面,我收到了服務端的響應數據包,說明我第一次發送的網路包成功到達服務端,這樣,我自己的發送和接收能力也是正常的。
第三次握手:客戶端發包,服務端收到了。這樣服務端就能得出結論:客戶端的接收、發送能力,服務端的發送、接收能力是正常的。第一、二次握手後,服務端並不知道客戶端的接收能力以及自己的發送能力是否正常。
而在第三次握手時,服務端收到了客戶端對第二次握手作的回應。從服務端的角度,我在第二次握手時的響應數據發送出去了,客戶端接收到了。所以,我的發送能力是正常的。而客戶端的接收能力也是正常的。
經歷了上面的三次握手過程,客戶端和服務端都確認了自己的接收、發送能力是正常的。之後就可以正常通訊了。
每次都是接收到數據包的一方可以得到一些結論,發送的一方其實沒有任何頭緒。我雖然有發包的動作,但是我怎麼知道我有沒有發出去,而對方有沒有接收到呢?
而從上面的過程可以看到,最少是需要三次握手過程的。兩次達不到讓雙方都得出自己、對方的接收、發送能力都正常的結論。
其實每次收到網路包的一方至少是可以得到:對方的發送、我方的接收是正常的。而每一步都是有關聯的,下一次的「響應」是由於第一次的「請求」觸發,因此每次握手其實是可以得到額外的結論的。
比如第三次握手時,服務端收到數據包,表明看服務端只能得到客戶端的發送能力、服務端的接收能力是正常的,但是結合第二次,說明服務端在第二次發送的響應包,客戶端接收到了,並且作出了響應,從而得到額外的結論:客戶端的接收、服務端的發送是正常的。
5、TCP 的四次揮手(為什麼四次?)
四次揮手:
- 客戶端發送一個 FIN 段,並包含一個希望接收者看到的自己當前的序列號 K. 同時還包含一個 ACK 表示確認對方最近一次發過來的數據。
- 服務端將 K 值加 1 作為 ACK 序號值,表明收到了上一個包。這時上層的應用程式會被告知另一端發起了關閉操作,通常這將引起應用程式發起自己的關閉操作。
- 服務端發起自己的 FIN 段,ACK=K+1, Seq=L。
- 客戶端確認。進入 TIME-WAIT 狀態,等待 2 MSL(最大報文存活時間)後釋放連接。ACK=L+1。
為什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?
1、TCP連接是雙向傳輸的對等的模式,就是說雙方都可以同時向對方發送或接收數據。當有一方要關閉連接時,會發送指令告知對方,我要關閉連接了。
2、這時對方會回一個ACK,此時一個方向的連接關閉。但是另一個方向仍然可以繼續傳輸數據,也就是說,服務端收到客戶端的 FIN 標誌,知道客戶端想要斷開這次連接了,但是,我服務端,我還想發數據呢?我等到發送完了所有的數據後,會發送一個 FIN 段來關閉此方向上的連接。接收方發送 ACK確認關閉連接。
注意,接收到FIN報文的一方只能回復一個ACK, 它是無法馬上返回對方一個FIN報文段的,因為結束數據傳輸的「指令」是上層應用層給出的,我只是一個「搬運工」,我無法了解「上層的意志」。
3、客戶端發送了 FIN 連接釋放報文之後,伺服器收到了這個報文,就進入了 CLOSE-WAIT 狀態。這個狀態是為了讓伺服器端發送還未傳送完畢的數據,傳送完畢之後,伺服器會發送 FIN 連接釋放報文。
4、因為服務端在 LISTEN 狀態下,收到建立連接請求的 SYN 報文後,把 ACK 和 SYN 放在一個報文里發送給客戶端。而關閉連接時,當收到對方的 FIN 報文時,僅僅表示對方不再發送數據了但是還能接收數據,己方是否現在關閉發送數據通道,需要上層應用來決定,因此,己方 ACK 和 FIN 一般都會分開發。
TIME_WAIT
客戶端接收到伺服器端的 FIN 報文後進入此狀態,此時並不是直接進入 CLOSED 狀態,還需要等待一個時間計時器設置的時間 2MSL。這麼做有兩個理由:
- 確保最後一個確認報文能夠到達。如果 B 沒收到 A 發送來的確認報文,那麼就會重新發送連接釋放請求報文,A 等待一段時間就是為了處理這種情況的發生。
- 等待一段時間是為了讓本連接持續時間內所產生的所有報文都從網路中消失,使得下一個新的連接不會出現舊的連接請求報文。
6、TCP 短連接和長連接的區別
短連接:Client 向 Server 發送消息,Server 回應 Client,然後一次讀寫就完成了,這時候雙方任何一個都可以發起 close 操作,不過一般都是 Client 先發起 close 操作。短連接一般只會在 Client/Server 間傳遞一次讀寫操作。
短連接的優點:管理起來比較簡單,建立存在的連接都是有用的連接,不需要額外的控制手段。
長連接:Client 與 Server 完成一次讀寫之後,它們之間的連接並不會主動關閉,後續的讀寫操作會繼續使用這個連接。
在長連接的應用場景下,Client 端一般不會主動關閉它們之間的連接,Client 與 Server 之間的連接如果一直不關閉的話,隨著客戶端連接越來越多,Server 壓力也越來越大,這時候 Server 端需要採取一些策略,如關閉一些長時間沒有讀寫事件發生的連接,這樣可以避免一些惡意連接導致 Server 端服務受損;如果條件再允許可以以客戶端為顆粒度,限制每個客戶端的最大長連接數,從而避免某個客戶端連累後端的服務。
長連接和短連接的產生在於 Client 和 Server 採取的關閉策略,具體的應用場景採用具體的策略。
7、TCP粘包、拆包及解決辦法
為什麼常說 TCP 有粘包和拆包的問題而不說 UDP ?
由前兩節可知,UDP 是基於報文發送的,UDP首部採用了 16bit 來指示 UDP 數據報文的長度,因此在應用層能很好的將不同的數據報文區分開,從而避免粘包和拆包的問題。
而 TCP 是基於位元組流的,雖然應用層和 TCP 傳輸層之間的數據交互是大小不等的數據塊,但是 TCP 並沒有把這些數據塊區分邊界,僅僅是一連串沒有結構的位元組流;另外從 TCP 的幀結構也可以看出,在 TCP 的首部沒有表示數據長度的欄位,基於上面兩點,在使用 TCP 傳輸數據時,才有粘包或者拆包現象發生的可能。
什麼是粘包、拆包?
假設 Client 向 Server 連續發送了兩個數據包,用 packet1 和 packet2 來表示,那麼服務端收到的數據可以分為三種情況,現列舉如下:
第一種情況,接收端正常收到兩個數據包,即沒有發生拆包和粘包的現象。
第二種情況,接收端只收到一個數據包,但是這一個數據包中包含了發送端發送的兩個數據包的資訊,這種現象即為粘包。這種情況由於接收端不知道這兩個數據包的界限,所以對於接收端來說很難處理。
第三種情況,這種情況有兩種表現形式,如下圖。接收端收到了兩個數據包,但是這兩個數據包要麼是不完整的,要麼就是多出來一塊,這種情況即發生了拆包和粘包。這兩種情況如果不加特殊處理,對於接收端同樣是不好處理的。
為什麼會發生 TCP 粘包、拆包?
- 要發送的數據大於 TCP 發送緩衝區剩餘空間大小,將會發生拆包。
- 待發送數據大於 MSS(最大報文長度),TCP 在傳輸前將進行拆包。
- 要發送的數據小於 TCP 發送緩衝區的大小,TCP 將多次寫入緩衝區的數據一次發送出去,將會發生粘包。
- 接收數據端的應用層沒有及時讀取接收緩衝區中的數據,將發生粘包。
粘包、拆包解決辦法
由於 TCP 本身是面向位元組流的,無法理解上層的業務數據,所以在底層是無法保證數據包不被拆分和重組的,這個問題只能通過上層的應用協議棧設計來解決,根據業界的主流協議的解決方案,歸納如下:
- 消息定長:發送端將每個數據包封裝為固定長度(不夠的可以通過補 0 填充),這樣接收端每次接收緩衝區中讀取固定長度的數據就自然而然的把每個數據包拆分開來。
- 設置消息邊界:服務端從網路流中按消息邊界分離出消息內容。在包尾增加回車換行符進行分割,例如 FTP 協議。
- 將消息分為消息頭和消息體:消息頭中包含表示消息總長度(或者消息體長度)的欄位。
- 更複雜的應用層協議比如 Netty 中實現的一些協議都對粘包、拆包做了很好的處理。
8、TCP 可靠傳輸
TCP 使用超時重傳來實現可靠傳輸:如果一個已經發送的報文段在超時時間內沒有收到確認,那麼就重傳這個報文段。
一個報文段從發送再到接收到確認所經過的時間稱為往返時間 RTT,加權平均往返時間 RTTs 計算如下:
其中,0 ≤ a < 1,RTTs 隨著 a 的增加更容易受到 RTT 的影響。超時時間 RTO 應該略大於 RTTs,TCP 使用的超時時間計算如下:
其中 RTTd 為偏差的加權平均值。
9、TCP 滑動窗口
窗口是快取的一部分,用來暫時存放位元組流。發送方和接收方各有一個窗口,接收方通過 TCP 報文段中的窗口欄位告訴發送方自己的窗口大小,發送方根據這個值和其它資訊設置自己的窗口大小。
發送窗口內的位元組都允許被發送,接收窗口內的位元組都允許被接收。如果發送窗口左部的位元組已經發送並且收到了確認,那麼就將發送窗口向右滑動一定距離,直到左部第一個位元組不是已發送並且已確認的狀態;接收窗口的滑動類似,接收窗口左部位元組已經發送確認並交付主機,就向右滑動接收窗口。
接收窗口只會對窗口內最後一個按序到達的位元組進行確認,例如接收窗口已經收到的位元組為 {31, 34, 35},其中 {31} 按序到達,而 {34, 35} 就不是,因此只對位元組 31 進行確認。發送方得到一個位元組的確認之後,就知道這個位元組之前的所有位元組都已經被接收。
10、TCP 流量控制
流量控制是為了控制發送方發送速率,保證接收方來得及接收。
接收方發送的確認報文中的窗口欄位可以用來控制發送方窗口大小,從而影響發送方的發送速率。將窗口欄位設置為 0,則發送方不能發送數據。
實際上,為了避免此問題的產生,發送端主機會時不時的發送一個叫做窗口探測的數據段,此數據段僅包含一個位元組來獲取最新的窗口大小資訊。
11、TCP 擁塞控制
如果網路出現擁塞,分組將會丟失,此時發送方會繼續重傳,從而導致網路擁塞程度更高。因此當出現擁塞時,應當控制發送方的速率。這一點和流量控制很像,但是出發點不同。流量控制是為了讓接收方能來得及接收,而擁塞控制是為了降低整個網路的擁塞程度。
TCP 主要通過四個演算法來進行擁塞控制:
慢開始、擁塞避免、快重傳、快恢復。
發送方需要維護一個叫做擁塞窗口(cwnd)的狀態變數,注意擁塞窗口與發送方窗口的區別:擁塞窗口只是一個狀態變數,實際決定發送方能發送多少數據的是發送方窗口。
為了便於討論,做如下假設:
- 接收方有足夠大的接收快取,因此不會發生流量控制;
- 雖然 TCP 的窗口基於位元組,但是這裡設窗口的大小單位為報文段。
慢開始與擁塞避免
發送的最初執行慢開始,令 cwnd = 1,發送方只能發送 1 個報文段;當收到確認後,將 cwnd 加倍,因此之後發送方能夠發送的報文段數量為:2、4、8 …
注意到慢開始每個輪次都將 cwnd 加倍,這樣會讓 cwnd 增長速度非常快,從而使得發送方發送的速度增長速度過快,網路擁塞的可能性也就更高。設置一個慢開始門限 ssthresh,當 cwnd >= ssthresh 時,進入擁塞避免,每個輪次只將 cwnd 加 1。
如果出現了超時,則令 ssthresh = cwnd / 2,然後重新執行慢開始。
快重傳與快恢復
在接收方,要求每次接收到報文段都應該對最後一個已收到的有序報文段進行確認。例如已經接收到 M1 和 M2,此時收到 M4,應當發送對 M2 的確認。
在發送方,如果收到三個重複確認,那麼可以知道下一個報文段丟失,此時執行快重傳,立即重傳下一個報文段。例如收到三個 M2,則 M3 丟失,立即重傳 M3。
在這種情況下,只是丟失個別報文段,而不是網路擁塞。因此執行快恢復,令 ssthresh = cwnd / 2 ,cwnd = ssthresh,注意到此時直接進入擁塞避免。
慢開始和快恢復的快慢指的是 cwnd 的設定值,而不是 cwnd 的增長速率。慢開始 cwnd 設定為 1,而快恢復 cwnd 設定為 ssthresh。
12、提供網路利用率
1、Nagle 演算法
發送端即使還有應該發送的數據,但如果這部分數據很少的話,則進行延遲發送的一種處理機制。具體來說,就是僅在下列任意一種條件下才能發送數據。如果兩個條件都不滿足,那麼暫時等待一段時間以後再進行數據發送。
- 已發送的數據都已經收到確認應答。
- 可以發送最大段長度的數據時。
2、延遲確認應答
接收方收到數據之後可以並不立即返回確認應答,而是延遲一段時間的機制。
- 在沒有收到 2*最大段長度的數據為止不做確認應答。
- 其他情況下,最大延遲 0.5秒 發送確認應答。
- TCP 文件傳輸中,大多數是每兩個數據段返回一次確認應答。
3、捎帶應答
在一個 TCP 包中既發送數據又發送確認應答的一種機制,由此,網路利用率會提高,電腦的負荷也會減輕,但是這種應答必須等到應用處理完數據並將作為回執的數據返回為止。
今天的知識點掌握了嗎?不要忘了學而時習之,不亦可乎。
歡迎留言和我交流~
參考:
www.cnblogs.com/panchanggui/p/9518735.html
www.cnblogs.com/qcrao-2018/p/10182185.html
github.com/CyC2018/CS-Notes/blob/master/docs/notes/