TCP 知識(shí)自我匯總 (三次握手和四次握手,流量控制以及擁塞控制原理)

1.TCP基本認(rèn)識(shí)

什么是 TCP ?

TCP 是面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。因?yàn)?TCP 是一個(gè)工作在傳輸層可靠數(shù)據(jù)傳輸?shù)姆?wù),它能確保接收端接收的網(wǎng)絡(luò)包是無(wú)損壞、無(wú)間隔、非冗余和按序的。

建立一個(gè) TCP 連接是需要客戶(hù)端與服務(wù)器端達(dá)成上述三個(gè)信息的共識(shí)。

Socket:由 IP 地址和端口號(hào)組成,序列號(hào):用來(lái)解決亂序問(wèn)題等,窗口大小:用來(lái)做流量控制

源地址和目的地址的字段(32位)是在 IP 頭部中,作用是通過(guò) IP 協(xié)議發(fā)送報(bào)文給對(duì)方主機(jī)。

源端口和目的端口的字段(16位)是在 TCP 頭部中,作用是告訴 TCP 協(xié)議應(yīng)該把報(bào)文發(fā)給哪個(gè)進(jìn)程


序列號(hào):在建立連接時(shí)由計(jì)算機(jī)生成的隨機(jī)數(shù)作為其初始值,通過(guò) SYN 包傳給接收端主機(jī),每發(fā)送一次數(shù)據(jù),就「累加」一次該「數(shù)據(jù)字節(jié)數(shù)」的大小。用來(lái)解決網(wǎng)絡(luò)包亂序問(wèn)題。

確認(rèn)應(yīng)答號(hào):指下一次「期望」收到的數(shù)據(jù)的序列號(hào),發(fā)送端收到這個(gè)確認(rèn)應(yīng)答以后可以認(rèn)為在這個(gè)序號(hào)以前的數(shù)據(jù)都已經(jīng)被正常接收。用來(lái)解決不丟包的問(wèn)題。

控制位:

ACK:該位為 1 時(shí),「確認(rèn)應(yīng)答」的字段變?yōu)橛行В琓CP 規(guī)定除了最初建立連接時(shí)的 SYN 包之外該位必須設(shè)置為 1 。

RST:該位為 1 時(shí),表示 TCP 連接中出現(xiàn)異常必須強(qiáng)制斷開(kāi)連接。

SYC:該位為 1 時(shí),表示希望建立連,并在其「序列號(hào)」的字段進(jìn)行序列號(hào)初始值的設(shè)定。

FIN:該位為 1 時(shí),表示今后不會(huì)再有數(shù)據(jù)發(fā)送,希望斷開(kāi)連接。當(dāng)通信結(jié)束希望斷開(kāi)連接時(shí),通信雙方的主機(jī)之間就可以相互交換 FIN 位置為 1 的 TCP 段。


2.TCP與UDP區(qū)別

UDP 和 TCP 有什么區(qū)別呢?分別的應(yīng)用場(chǎng)景是?

UDP 不提供復(fù)雜的控制機(jī)制,利用 IP 提供面向「無(wú)連接」的通信服務(wù)。

UDP 協(xié)議真的非常簡(jiǎn)單,頭部只有 8 個(gè)字節(jié)( 64 位),UDP 的頭部格式如下:


1. 連接

TCP 是面向連接的傳輸層協(xié)議,傳輸數(shù)據(jù)前先要建立連接。UDP 是不需要連接,即刻傳輸數(shù)據(jù)。

2. 服務(wù)對(duì)象

TCP 是一對(duì)一的兩點(diǎn)服務(wù),即一條連接只有兩個(gè)端點(diǎn)。UDP 支持一對(duì)一、一對(duì)多、多對(duì)多的交互通信

3. 可靠性

TCP 是可靠交付數(shù)據(jù)的,數(shù)據(jù)可以無(wú)差錯(cuò)、不丟失、不重復(fù)、按需到達(dá)。UDP 是盡最大努力交付,不保證可靠交付數(shù)據(jù)。

4. 擁塞控制、流量控制

TCP 有擁塞控制和流量控制機(jī)制,保證數(shù)據(jù)傳輸?shù)陌踩浴?

UDP 則沒(méi)有,即使網(wǎng)絡(luò)非常擁堵了,也不會(huì)

影響 UDP 的發(fā)送速率。

5. 首部開(kāi)銷(xiāo)

TCP 首部長(zhǎng)度較長(zhǎng),會(huì)有一定的開(kāi)銷(xiāo),首部在沒(méi)有使用「選項(xiàng)」字段時(shí)是 20 個(gè)字節(jié),如果使用了「選項(xiàng)」字段則會(huì)變長(zhǎng)的。

UDP 首部只有 8 個(gè)字節(jié),并且是固定不變的,開(kāi)銷(xiāo)較小。

TCP 和 UDP 應(yīng)用場(chǎng)景:

由于 TCP 是面向連接,能保證數(shù)據(jù)的可靠性交付,因此經(jīng)常用于:

FTP 文件傳輸,HTTP / HTTPS

由于 UDP 面向無(wú)連接,它可以隨時(shí)發(fā)送數(shù)據(jù),再加上UDP本身的處理既簡(jiǎn)單又高效,因此經(jīng)常用于:

包總量較少的通信,如 DNS 、SNMP 等。 視頻、音頻等多媒體通信和廣播通信

為什么 UDP 頭部沒(méi)有「首部長(zhǎng)度」字段,而 TCP 頭部有「首部長(zhǎng)度」字段呢?

原因是 TCP 有可變長(zhǎng)的「選項(xiàng)」字段,而 UDP 頭部長(zhǎng)度則是不會(huì)變化的,無(wú)需多一個(gè)字段去記錄 UDP 的首部長(zhǎng)度。

為什么 UDP 頭部有「包長(zhǎng)度」字段,而 TCP 頭部則沒(méi)有「包長(zhǎng)度」字段呢?

先說(shuō)說(shuō) TCP 是如何計(jì)算負(fù)載數(shù)據(jù)長(zhǎng)度:TCP數(shù)據(jù)長(zhǎng)度 = IP總長(zhǎng)度 - IP首部長(zhǎng)度 - TCP首部長(zhǎng)度

TCP 三次握手過(guò)程和狀態(tài)變遷

TCP 是面向連接的協(xié)議,所以使用 TCP 前必須先建立連接,而建立連接是通過(guò)三次握手而進(jìn)行的

一開(kāi)始,客戶(hù)端和服務(wù)端都處于 CLOSED 狀態(tài)。先是服務(wù)端主動(dòng)監(jiān)聽(tīng)某個(gè)端口,處于 LISTEN 狀態(tài)


第一個(gè)報(bào)文:SYN報(bào)文
客戶(hù)端會(huì)隨機(jī)初始化序號(hào)(client_isn),將此序號(hào)置于 TCP 首部的序號(hào)字段中,同時(shí)把 SYN 標(biāo)志位置為 1 。表示 SYN 報(bào)文。接著把第一個(gè) SYN 報(bào)文發(fā)送給服務(wù)端,表示向服務(wù)端發(fā)起連接,該報(bào)文不包含應(yīng)用層數(shù)據(jù),之后客戶(hù)端處于SYN-SENT 狀態(tài)。


第二個(gè)報(bào)文 —— SYN + ACK 報(bào)文
服務(wù)端收到客戶(hù)端的 SYN 報(bào)文后,首先服務(wù)端也隨機(jī)初始化自己的序號(hào)(server_isn),將此序號(hào)填入 TCP 首部的「序號(hào)」字段中,其次把 TCP 首部的「確認(rèn)應(yīng)答號(hào)」字段填入 client_isn + 1, 接著把 SYN 和 ACK 標(biāo)志位置為1。最后把該報(bào)文發(fā)給客戶(hù)端,該報(bào)文也不包含應(yīng)用層數(shù)據(jù),之后服務(wù)端處于SYN-RCVD 狀態(tài)。


第三個(gè)報(bào)文 —— ACK 報(bào)文
客戶(hù)端收到服務(wù)端報(bào)文后,還要向服務(wù)端回應(yīng)最后一個(gè)應(yīng)答報(bào)文,首先該應(yīng)答報(bào)文 TCP 首部 ACK 標(biāo)志位置為 1 ,其次「確認(rèn)應(yīng)答號(hào)」字段填入 server_isn + 1,最后把報(bào)文發(fā)送給服務(wù)端,這次報(bào)文可以攜帶客戶(hù)到服務(wù)器的數(shù)據(jù),之后客戶(hù)端處于 ESTABLISHED 狀態(tài)。

服務(wù)器收到客戶(hù)端的應(yīng)答報(bào)文后,也進(jìn)入 ESTABLISHED 狀態(tài)。

從上面的過(guò)程可以發(fā)現(xiàn)第三次握手是可以攜帶數(shù)據(jù)的,前兩次握手是不可以攜帶數(shù)據(jù)的,這也是面試常問(wèn)的題。

一旦完成三次握手,雙方都處于 ESTABLISHED 狀態(tài),此致連接就已建立完成,客戶(hù)端和服務(wù)端就可以相互發(fā)送數(shù)據(jù)了。

如何在 Linux 系統(tǒng)中查看 TCP 狀態(tài)?

TCP 的連接狀態(tài)查看,在 Linux 可以通過(guò) netstat -napt 命令查看。


為什么是三次握手?不是兩次、四次?

接下來(lái)以三個(gè)方面分析三次握手的原因:

三次握手才可以阻止歷史重復(fù)連接的初始化(主要原因)

三次握手才可以同步雙方的初始序列號(hào)

三次握手才可以避免資源浪費(fèi)

原因一:避免歷史連接

三次握手的首要原因是為了防止舊的重復(fù)連接初始化造成混亂。

客戶(hù)端連續(xù)發(fā)送多次 SYN 建立連接的報(bào)文,在網(wǎng)絡(luò)擁堵等情況下:

一個(gè)「舊 SYN 報(bào)文」比「最新的 SYN 」 報(bào)文早到達(dá)了服務(wù)端;

那么此時(shí)服務(wù)端就會(huì)回一個(gè) SYN + ACK 報(bào)文給客戶(hù)端;

客戶(hù)端收到后可以根據(jù)自身的上下文,判斷這是一個(gè)歷史連接(序列號(hào)過(guò)期或超時(shí)),那么客戶(hù)端就會(huì)發(fā)送 RST 報(bào)文給服務(wù)端,表示中止這一次連接。

如果是兩次握手連接,就不能判斷當(dāng)前連接是否是歷史連接,三次握手則可以在客戶(hù)端(發(fā)送方)準(zhǔn)備發(fā)送第三次報(bào)文時(shí),客戶(hù)端因有足夠的上下文來(lái)判斷當(dāng)前連接是否是歷史連接:

如果是歷史連接(序列號(hào)過(guò)期或超時(shí)),則第三次握手發(fā)送的報(bào)文是 RST 報(bào)文,以此中止歷史連接;如果不是歷史連接,則第三次發(fā)送的報(bào)文是 ACK 報(bào)文,通信雙方就會(huì)成功建立連接;所以, TCP 使用三次握手建立連接的最主要原因是防止歷史連接初始化了連接。

原因二:同步雙方初始序列號(hào)

TCP 協(xié)議的通信雙方, 都必須維護(hù)一個(gè)「序列號(hào)」, 序列號(hào)是可靠傳輸?shù)囊粋€(gè)關(guān)鍵因素,它的作用:

接收方可以去除重復(fù)的數(shù)據(jù);

接收方可以根據(jù)數(shù)據(jù)包的序列號(hào)按序接收;

可以標(biāo)識(shí)發(fā)送出去的數(shù)據(jù)包中, 哪些是已經(jīng)被對(duì)方收到的;

序列號(hào)在 TCP 連接中占據(jù)著非常重要的作用,所以當(dāng)客戶(hù)端發(fā)送攜帶「初始序列號(hào)」的 SYN 報(bào)文的時(shí)候,需要服務(wù)端回一個(gè) ACK 應(yīng)答報(bào)文,表示客戶(hù)端的 SYN 報(bào)文已被服務(wù)端成功接收,那當(dāng)服務(wù)端發(fā)送「初始序列號(hào)」給客戶(hù)端的時(shí)候,依然也要得到客戶(hù)端的應(yīng)答回應(yīng),這樣一來(lái)一回,才能確保雙方的初始序列號(hào)能被可靠的同步。

原因三:避免資源浪費(fèi)

如果只有「兩次握手」,當(dāng)客戶(hù)端的 SYN 請(qǐng)求連接在網(wǎng)絡(luò)中阻塞,客戶(hù)端沒(méi)有接收到ACK 報(bào)文,就會(huì)重新發(fā)送 SYN ,由于沒(méi)有第三次握手,服務(wù)器不清楚客戶(hù)端是否收到了自己發(fā)送的建立連接的 ACK 確認(rèn)信號(hào),所以每收到一個(gè) SYN 就只能先主動(dòng)建立一個(gè)連接,這會(huì)造成什么情況呢?

如果客戶(hù)端的 SYN 阻塞了,重復(fù)發(fā)送多次 SYN 報(bào)文,那么服務(wù)器在收到請(qǐng)求后就會(huì)建立多個(gè)冗余的無(wú)效鏈接,造成不必要的資源浪費(fèi)。

MTU 與 MSS

MTU:一個(gè)網(wǎng)絡(luò)包的最大長(zhǎng)度,以太網(wǎng)中一般為 1500 字節(jié);

MSS:除去 IP 和 TCP 頭部之后,一個(gè)網(wǎng)絡(luò)包所能容納的 TCP 數(shù)據(jù)的最大長(zhǎng)度;

如果TCP 的整個(gè)報(bào)文(頭部 + 數(shù)據(jù))交給 IP 層進(jìn)行分片,會(huì)有什么異常呢?

當(dāng) IP 層有一個(gè)超過(guò) MTU 大小的數(shù)據(jù)(TCP 頭部 + TCP 數(shù)據(jù))要發(fā)送,那么 IP 層就要進(jìn)行分片,把數(shù)據(jù)分片成若干片,保證每一個(gè)分片都小于 MTU。把一份 IP 數(shù)據(jù)報(bào)進(jìn)行分片以后,由目標(biāo)主機(jī)的 IP 層來(lái)進(jìn)行重新組裝后,在交給上一層 TCP 傳輸層。

這看起來(lái)井然有序,但這存在隱患的,那么當(dāng)如果一個(gè) IP 分片丟失,整個(gè) IP 報(bào)文的所有分片都得重傳

因?yàn)?IP 層本身沒(méi)有超時(shí)重傳機(jī)制,它由傳輸層的 TCP 來(lái)負(fù)責(zé)超時(shí)和重傳。

當(dāng)接收方發(fā)現(xiàn) TCP 報(bào)文(頭部 + 數(shù)據(jù))的某一片丟失后,則不會(huì)響應(yīng) ACK 給對(duì)方,那么發(fā)送方的 TCP 在超時(shí)后,就會(huì)重發(fā)「整個(gè) TCP 報(bào)文(頭部 + 數(shù)據(jù))」。

因此,可以得知由 IP 層進(jìn)行分片傳輸,是非常沒(méi)有效率的。

所以,為了達(dá)到最佳的傳輸效能 TCP 協(xié)議在建立連接的時(shí)候通常要協(xié)商雙方的 MSS 值,當(dāng) TCP 層發(fā)現(xiàn)數(shù)據(jù)超過(guò) MSS 時(shí),則就先會(huì)進(jìn)行分片,當(dāng)然由它形成的 IP 包的長(zhǎng)度也就不會(huì)大于 MTU ,自然也就不用 IP 分片了。

什么是 SYN 攻擊?如何避免 SYN 攻擊?

SYN 攻擊

我們都知道 TCP 連接建立是需要三次握手,假設(shè)攻擊者短時(shí)間偽造不同 IP 地址的SYN 報(bào)文,服務(wù)端每接收到一個(gè) SYN 報(bào)文,就進(jìn)入SYN_RCVD 狀態(tài),但服務(wù)端發(fā)送出去的 ACK + SYN 報(bào)文,無(wú)法得到未知 IP 主機(jī)的 ACK 應(yīng)答,久而久之就會(huì)占滿(mǎn)服務(wù)端的 SYN 接收隊(duì)列(未連接隊(duì)列),使得服務(wù)器不能為正常用戶(hù)服務(wù)。


避免 SYN 攻擊方式一

其中一種解決方式是通過(guò)修改 Linux 內(nèi)核參數(shù),控制隊(duì)列大小和當(dāng)隊(duì)列滿(mǎn)時(shí)應(yīng)做什么處理。

當(dāng)網(wǎng)卡接收數(shù)據(jù)包的速度大于內(nèi)核處理的速度時(shí),會(huì)有一個(gè)隊(duì)列保存這些數(shù)據(jù)包。控制該隊(duì)列的最大值如下參數(shù):

net.core.netdev_max_backlog

SYN_RCVD 狀態(tài)連接的最大個(gè)數(shù):

net.ipv4.tcp_max_syn_backlog

超出處理能時(shí),對(duì)新的 SYN 直接回 RST,丟棄連接:

net.ipv4.tcp_abort_on_overflow

四次揮手都做什么?

TCP的連接是全雙工的,所以連接的拆除需要單獨(dú)將兩個(gè)通道分別拆除,而四次揮手所做的事情就是拆除兩條通道釋放資源

TCP 提供了連接的一端結(jié)束他的發(fā)送后,還能接收來(lái)自另一端數(shù)據(jù)的能力,也就是所謂的半關(guān)閉。


這里以Client作為主動(dòng)發(fā)起端,Server作為被動(dòng)關(guān)閉端。

第一步,Client主動(dòng)發(fā)起一個(gè)Req給Server,里面包含F(xiàn)IN標(biāo)識(shí)位=1,CLient的Seq序列號(hào)N,表示的是當(dāng)前Client在該連接上的當(dāng)前序列號(hào)。

第二步,Server端在收到這個(gè)含有FIN的Req消息之后,校驗(yàn)無(wú)誤之后會(huì)立馬回復(fù)ACK消息給CLient端,消息內(nèi)部包含ACK標(biāo)志位為1,同時(shí)Seq號(hào)碼是FIN的請(qǐng)求消息的Seq號(hào)+1。此時(shí)的Sever同時(shí)會(huì)主動(dòng)發(fā)個(gè)結(jié)束標(biāo)識(shí)給Server上面的應(yīng)用層程序,應(yīng)用層程序可以決定是立馬結(jié)束,還是等到服務(wù)其上面的該連接中的數(shù)據(jù)處理完了之后,在發(fā)送FIN消息給Client來(lái)關(guān)掉另外的一半連接。

第三步,Server端在處理完該連接上面的Pending住的數(shù)據(jù)之后,應(yīng)用程序會(huì)close這個(gè)連接。Client會(huì)主動(dòng)發(fā)起FIN的Req消息給Client端。消息內(nèi)部帶有,F(xiàn)IN=1的結(jié)束符標(biāo)識(shí)位,以及Server端的Seq序列號(hào)。

第四步,Client端在收到對(duì)應(yīng)的FIN消息之后,會(huì)主動(dòng)通知應(yīng)用層程序,告知這個(gè)連接現(xiàn)在需要關(guān)閉了。然后,Client會(huì)回復(fù)ACK消息給Server,以便斷開(kāi)另外一個(gè)方向的通道,這個(gè)消息包含ACK=1的標(biāo)識(shí)位和FIN的REQ帶過(guò)來(lái)的Seq+1。

為什么揮手需要四次?

因?yàn)門(mén)CP是一個(gè)全雙工協(xié)議,必須單獨(dú)拆除每一條信道。4次揮手的目的是終止數(shù)據(jù)傳輸,并回收資源,此時(shí)兩個(gè)端點(diǎn)兩個(gè)方向的序列號(hào)已經(jīng)沒(méi)有了任何關(guān)系,必須等待兩方向都沒(méi)有數(shù)據(jù)傳輸時(shí)才能拆除虛鏈路,不像初始化時(shí)那么簡(jiǎn)單,發(fā)現(xiàn)SYN標(biāo)志就初始化一個(gè)序列號(hào)并確認(rèn)SYN的序列號(hào)。因此必須單獨(dú)分別在一個(gè)方向上終止該方向的數(shù)據(jù)傳輸。

再來(lái)回顧下四次揮手雙方發(fā) FIN 包的過(guò)程,就能理解為什么需要四次了。

關(guān)閉連接時(shí),客戶(hù)端向服務(wù)端發(fā)送 FIN 時(shí),僅僅表示客戶(hù)端不再發(fā)送數(shù)據(jù)了但是還能接收數(shù)據(jù)。

服務(wù)器收到客戶(hù)端的 FIN 報(bào)文時(shí),先回一個(gè) ACK 應(yīng)答報(bào)文,而服務(wù)端可能還有數(shù)據(jù)需要處理和發(fā)送,等服務(wù)端不再發(fā)送數(shù)據(jù)時(shí),才發(fā)送 FIN 報(bào)文給客戶(hù)端來(lái)表示同意現(xiàn)在關(guān)閉連接。

從上面過(guò)程可知,服務(wù)端通常需要等待完成數(shù)據(jù)的發(fā)送和處理,所以服務(wù)端的 ACK 和FIN 一般都會(huì)分開(kāi)發(fā)送,從而比三次握手導(dǎo)致多了一次。

四次揮手的狀態(tài)轉(zhuǎn)換?

Client端:

ESTABLISHED---發(fā)送FIN給Server-->FIN_WAIT_1---接收到Server端的FIN對(duì)應(yīng)的ACK-->FIN_WAIT_2---收到Server端發(fā)送過(guò)來(lái)的FIN消息-->FIIN_WAIT--2MSL之后會(huì)進(jìn)入-->CLOSED

Server端:

ESTABLISHED---接收到Client端的FIN->CLOSED_WAIT--Server端的應(yīng)用程序關(guān)閉發(fā)送FIN--> LAST_ACK---收到Client對(duì)于FIN的ACK響應(yīng)-->FIIN_WAIT---->CLOSED

最簡(jiǎn)單的理解

一:建立TCP連接:三次握手協(xié)議


客戶(hù)端:我要對(duì)你講話(huà),你能聽(tīng)到嗎;

服務(wù)端:我能聽(tīng)到;而且我也要對(duì)你講話(huà),你能聽(tīng)到嗎;

客戶(hù)端:我也能聽(tīng)到。

…….

互相開(kāi)始通話(huà)

……..? ?

二:關(guān)閉TCP連接:四次握手協(xié)議

客戶(hù)端:我說(shuō)完了,我要閉嘴了;

服務(wù)端:我收到請(qǐng)求,我要閉耳朵了;

(客戶(hù)端收到這個(gè)確認(rèn),于是安心地閉嘴了。)

…….

? ? 服務(wù)端還沒(méi)傾訴完自己的故事,于是繼續(xù)嘮嘮叨叨向客戶(hù)端說(shuō)了半天,直到說(shuō)完為止

…….

服務(wù)端:我說(shuō)完了,我也要閉嘴了;

客戶(hù)端:我收到請(qǐng)求,我要閉耳朵了;(事實(shí)上,客戶(hù)端為了保證這個(gè)確認(rèn)包成功送達(dá),等待了兩個(gè)最大報(bào)文生命周期后,才閉上耳朵。)

(服務(wù)端收到這個(gè)確認(rèn),于是安心地閉嘴了。)

TCP如何流量控制

TCP 為了防止發(fā)送方無(wú)腦的發(fā)送數(shù)據(jù),導(dǎo)致接收方緩沖區(qū)被填滿(mǎn),所以就有了滑動(dòng)窗口的機(jī)制,它可利用接收方的接收窗口來(lái)控制發(fā)送方要發(fā)送的數(shù)據(jù)量,也就是流量控制。

接收窗口是由接收方指定的值,存儲(chǔ)在 TCP 頭部中,它可以告訴發(fā)送方自己的 TCP 緩沖空間區(qū)大小,這個(gè)緩沖區(qū)是給應(yīng)用程序讀取數(shù)據(jù)的空間:

如果應(yīng)用程序讀取了緩沖區(qū)的數(shù)據(jù),那么緩沖空間區(qū)的就會(huì)把被讀取的數(shù)據(jù)移除

如果應(yīng)用程序沒(méi)有讀取數(shù)據(jù),則數(shù)據(jù)會(huì)一直滯留在緩沖區(qū)。

接收窗口的大小,是在 TCP 三次握手中協(xié)商好的,后續(xù)數(shù)據(jù)傳輸時(shí),接收方發(fā)送確認(rèn)應(yīng)答 ACK 報(bào)文時(shí),會(huì)攜帶當(dāng)前的接收窗口的大小,以此來(lái)告知發(fā)送方。

假設(shè)接收方接收到數(shù)據(jù)后,應(yīng)用層能很快的從緩沖區(qū)里讀取數(shù)據(jù),那么窗口大小會(huì)一直保持不變,過(guò)程如下:


但是現(xiàn)實(shí)中服務(wù)器會(huì)出現(xiàn)繁忙的情況,當(dāng)應(yīng)用程序讀取速度慢,那么緩存空間會(huì)慢慢被占滿(mǎn),于是為了保證發(fā)送方發(fā)送的數(shù)據(jù)不會(huì)超過(guò)緩沖區(qū)大小,則服務(wù)器會(huì)調(diào)整窗口大小的值,接著通過(guò) ACK 報(bào)文通知給對(duì)方,告知現(xiàn)在的接收窗口大小,從而控制發(fā)送方發(fā)送的數(shù)據(jù)大小。


零窗口通知與窗口探測(cè)

假設(shè)接收方處理數(shù)據(jù)的速度跟不上接收數(shù)據(jù)的速度,緩存就會(huì)被占滿(mǎn),從而導(dǎo)致接收窗口為 0,當(dāng)發(fā)送方接收到零窗口通知時(shí),就會(huì)停止發(fā)送數(shù)據(jù)。

如下圖,可以接收方的窗口大小在不斷的收縮至 0:

窗口大小在收縮

接著,發(fā)送方會(huì)定時(shí)發(fā)送窗口大小探測(cè)報(bào)文,以便及時(shí)知道接收方窗口大小的變化。

以下圖 Wireshark 分析圖作為例子說(shuō)明:

零窗口 與 窗口探測(cè)

發(fā)送方發(fā)送了數(shù)據(jù)包 1 給接收方,接收方收到后,由于緩沖區(qū)被占滿(mǎn),回了個(gè)零窗口通知;

發(fā)送方收到零窗口通知后,就不再發(fā)送數(shù)據(jù)了,直到過(guò)了 3.4 秒后,發(fā)送了一個(gè) TCP Keep-Alive 報(bào)文,也就是窗口大小探測(cè)報(bào)文;

當(dāng)接收方收到窗口探測(cè)報(bào)文后,就立馬回一個(gè)窗口通知,但是窗口大小還是 0;

發(fā)送方發(fā)現(xiàn)窗口還是 0,于是繼續(xù)等待了 6.8(翻倍) 秒后,又發(fā)送了窗口探測(cè)報(bào)文,接收方依然還是回了窗口為 0 的通知;

發(fā)送方發(fā)現(xiàn)窗口還是 0,于是繼續(xù)等待了 13.5(翻倍) 秒后,又發(fā)送了窗口探測(cè)報(bào)文,接收方依然還是回了窗口為 0 的通知;

可以發(fā)現(xiàn),這些窗口探測(cè)報(bào)文以 3.4s、6.5s、13.5s 的間隔出現(xiàn),說(shuō)明超時(shí)時(shí)間會(huì)翻倍遞增。

TCP 的擁塞控制原理


什么是 TCP 擁塞控制

TCP 擁塞控制的目標(biāo)是最大化利用網(wǎng)絡(luò)上瓶頸鏈路的帶寬。

Reno

Reno 被許多教材(例如:《計(jì)算機(jī)網(wǎng)絡(luò)——自頂向下的方法》)所介紹,適用于低延時(shí)、低帶寬的網(wǎng)絡(luò),它將擁塞控制的過(guò)程分為四個(gè)階段:慢啟動(dòng)、擁塞避免、快重傳和快恢復(fù),對(duì)應(yīng)的狀態(tài)如下所示:

慢啟動(dòng)階段思路是不要一開(kāi)始就發(fā)送大量的數(shù)據(jù),先探測(cè)一下網(wǎng)絡(luò)的擁塞程度,也就是說(shuō)由小到大逐漸增加擁塞窗口的大小,在沒(méi)有出現(xiàn)丟包時(shí)每收到一個(gè) ACK 就將擁塞窗口大小加一(單位是 MSS,最大單個(gè)報(bào)文段長(zhǎng)度),每輪次發(fā)送窗口增加一倍,呈指數(shù)增長(zhǎng),若出現(xiàn)丟包,則將擁塞窗口減半,進(jìn)入擁塞避免階段;

當(dāng)窗口達(dá)到慢啟動(dòng)閾值或出現(xiàn)丟包時(shí),進(jìn)入擁塞避免階段,窗口每輪次加一,呈線性增長(zhǎng);當(dāng)收到對(duì)一個(gè)報(bào)文的三個(gè)重復(fù)的 ACK 時(shí),認(rèn)為這個(gè)報(bào)文的下一個(gè)報(bào)文丟失了,進(jìn)入快重傳階段,要求接收方在收到一個(gè)失序的報(bào)文段后就立即發(fā)出重復(fù)確認(rèn)(為的是使發(fā)送方及早知道有報(bào)文段沒(méi)有到達(dá)對(duì)方,可提高網(wǎng)絡(luò)吞吐量約20%)而不要等到自己發(fā)送數(shù)據(jù)時(shí)捎帶確認(rèn);

快重傳完成后進(jìn)入快恢復(fù)階段,將慢啟動(dòng)閾值修改為當(dāng)前擁塞窗口值的一半,同時(shí)擁塞窗口值等于慢啟動(dòng)閾值,然后進(jìn)入擁塞避免階段,重復(fù)上述過(guò)程。

BBR 算法不將出現(xiàn)丟包或時(shí)延增加作為擁塞的信號(hào),而是認(rèn)為當(dāng)網(wǎng)絡(luò)上的數(shù)據(jù)包總量大于瓶頸鏈路帶寬和時(shí)延的乘積時(shí)才出現(xiàn)了擁塞,所以 BBR 也稱(chēng)為基于擁塞的擁塞控制算法(Congestion-Based Congestion Control),其適用網(wǎng)絡(luò)為高帶寬、高時(shí)延、有一定丟包率的長(zhǎng)肥網(wǎng)絡(luò),可以有效降低傳輸時(shí)延,并保證較高的吞吐量,

BBR 算法周期性地探測(cè)網(wǎng)絡(luò)的容量,交替測(cè)量一段時(shí)間內(nèi)的帶寬極大值和時(shí)延極小值,將其乘積作為作為擁塞窗口大小,使得擁塞窗口始的值始終與網(wǎng)絡(luò)的容量保持一致。

在有一定丟包率的網(wǎng)絡(luò)鏈路上充分利用帶寬。降低網(wǎng)絡(luò)鏈路上的 buffer 占用率,從而降低延遲。

基于丟包的擁塞控制:將丟包視為出現(xiàn)擁塞,采取緩慢探測(cè)的方式,逐漸增大擁塞窗口,當(dāng)出現(xiàn)丟包時(shí),將擁塞窗口減小,如 Reno、Cubic 等。

基于時(shí)延的擁塞控制:將時(shí)延增加視為出現(xiàn)擁塞,延時(shí)增加時(shí)增大擁塞窗口,延時(shí)減小時(shí)減小擁塞窗口,如 Vegas、FastTCP 等。

基于鏈路容量的擁塞控制:實(shí)時(shí)測(cè)量網(wǎng)絡(luò)帶寬和時(shí)延,認(rèn)為網(wǎng)絡(luò)上報(bào)文總量大于帶寬時(shí)延乘積時(shí)出現(xiàn)了擁塞,如 BBR。

基于學(xué)習(xí)的擁塞控制:沒(méi)有特定的擁塞信號(hào),而是借助評(píng)價(jià)函數(shù),基于訓(xùn)練數(shù)據(jù),使用機(jī)器學(xué)習(xí)的方法形成一個(gè)控制策略,如 Remy。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。