文章來(lái)源簡(jiǎn)書(shū):http://www.reibang.com/p/eab86c0d1612
上一篇博客闡述了TCP/IP五層網(wǎng)絡(luò)結(jié)構(gòu)模型以及一些關(guān)于TCP、UDP的基礎(chǔ)知識(shí),這篇博客會(huì)接著寫(xiě)一些關(guān)于TCP擁塞控制的算法以及對(duì)TCP中常有的疑問(wèn)進(jìn)行解答。
TCP擁塞控制
首先了解幾個(gè)概念封拧,為下面的敘述做鋪墊
- 擁塞窗口(cwnd):TCP擁塞控制中的主要參數(shù)图毕,表示發(fā)送端下一次最多可以發(fā)送的數(shù)據(jù)分包的個(gè)數(shù),是來(lái)自發(fā)送端的流量控制产镐。
-
接收端窗口(rwnd):又稱通知窗口(
Advertise Window
),接受端目前每次所能接收的數(shù)據(jù)分組的最大個(gè)數(shù)踢步,是來(lái)自接收端的流量控制癣亚。 - 慢開(kāi)始門(mén)限(ssthresh):當(dāng)擁塞窗口增長(zhǎng)到慢開(kāi)始門(mén)限時(shí),啟動(dòng)擁塞避免算法(后面會(huì)具體闡述)获印。
- 擁塞控制常用算法:慢開(kāi)始述雾、擁塞避免、快重傳兼丰、快恢復(fù)玻孟。
最初,發(fā)送方不知道接收方的容納能力鳍征,如果初次就發(fā)送較大量的數(shù)據(jù)黍翎,極有可能造成整個(gè)網(wǎng)絡(luò)的癱瘓,TCP為了防止此類問(wèn)題的出現(xiàn)艳丛,在通信一開(kāi)始就會(huì)通過(guò)一個(gè)叫慢開(kāi)始的算法得到的數(shù)值匣掸,對(duì)發(fā)送數(shù)據(jù)量進(jìn)行控制。
慢開(kāi)始:
由于需要考慮擁塞控制和流量控制兩個(gè)方面的內(nèi)容氮双,發(fā)送端的發(fā)送窗口為min(cwnd旺聚,rwnd)
,但是rwnd
是由對(duì)端確定的眶蕉,網(wǎng)絡(luò)環(huán)境對(duì)其沒(méi)有影響砰粹,所以在考慮擁塞的時(shí)候我們一般不考慮rwnd
的值,我們暫時(shí)只討論如何確定cwnd
值的大小。
在執(zhí)行慢開(kāi)始算法時(shí)碱璃,擁塞窗口 cwnd
的初始值為 1弄痹,發(fā)送第一個(gè)報(bào)文段。當(dāng)發(fā)送端收到來(lái)自接收端的ACK
之后嵌器,擁塞窗口開(kāi)始以1肛真、2、4這樣的指數(shù)形式增長(zhǎng)爽航。當(dāng)擁塞窗口cwnd
增長(zhǎng)到慢開(kāi)始門(mén)限值 ssthresh
時(shí)蚓让,就改為執(zhí)行擁塞避免算法,擁塞窗口按線性規(guī)律增長(zhǎng)讥珍。
擁塞避免:
最初历极,擁塞窗口指數(shù)增長(zhǎng),可以很快進(jìn)行大數(shù)據(jù)的發(fā)送衷佃,最大限度的利用網(wǎng)絡(luò)寬帶資源趟卸。當(dāng)達(dá)到慢開(kāi)始門(mén)限值,開(kāi)始進(jìn)入擁塞避免階段氏义,擁塞窗口開(kāi)始加法增加锄列。這樣就可以避免增長(zhǎng)過(guò)快導(dǎo)致網(wǎng)絡(luò)擁塞,慢慢的增加調(diào)整到網(wǎng)絡(luò)的最佳值惯悠。
快重傳與快速恢復(fù)
上面都沒(méi)有遇到網(wǎng)絡(luò)擁塞邻邮,當(dāng)真正遇到網(wǎng)絡(luò)擁塞時(shí),如何處理呢克婶?當(dāng)發(fā)送方連續(xù)收到三個(gè)重復(fù)的ACK之后筒严,TCP會(huì)進(jìn)入快速重傳、快速恢復(fù)的階段鸠补。
為什么叫快速重傳呢萝风?如果當(dāng)發(fā)送端接收到三個(gè)重復(fù)的確認(rèn)ACK時(shí)嘀掸,則斷定分組丟失紫岩,立即重傳丟失的報(bào)文段,而不必等待重傳計(jì)時(shí)器超時(shí)睬塌,相比之下泉蝌,前者速度更快。
快速恢復(fù)的主要步驟:
- 當(dāng)收到3個(gè)重復(fù)ACK時(shí)揩晴,把
ssthresh
設(shè)置為cwnd
的一半勋陪,把cwnd
設(shè)置為ssthresh
的值加3,然后重傳丟失的報(bào)文段硫兰。 - 再收到重復(fù)的ACK時(shí)诅愚,擁塞窗口增加1。
- 當(dāng)收到新的數(shù)據(jù)包的ACK時(shí)劫映,把
cwnd
設(shè)置為第一步中的ssthresh
的值违孝。原因是因?yàn)樵揂CK確認(rèn)了新的數(shù)據(jù)刹前,說(shuō)明從重復(fù)ACK時(shí)的數(shù)據(jù)都已收到,該恢復(fù)過(guò)程已經(jīng)結(jié)束雌桑,可以回到恢復(fù)之前的狀態(tài)了喇喉,也即再次進(jìn)入擁塞避免狀態(tài)。
當(dāng)TCP通信開(kāi)始之后校坑,網(wǎng)絡(luò)吞吐量會(huì)逐漸上升拣技,但是隨著網(wǎng)絡(luò)擁堵的發(fā)生,吞吐量也會(huì)急速下降耍目。于是會(huì)再次進(jìn)入吞吐量慢慢上升的過(guò)程膏斤。但是所謂TCP的吞吐量的特點(diǎn)就好像是在逐漸占領(lǐng)網(wǎng)絡(luò)寬帶的感覺(jué)。
TCP疑問(wèn)解答
為什么采用三次握手而不是二次握手制妄?
client
發(fā)出的第一個(gè)連接請(qǐng)求報(bào)文段并沒(méi)有丟失掸绞,而是在某個(gè)網(wǎng)絡(luò)結(jié)點(diǎn)長(zhǎng)時(shí)間的滯留了,以致延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)server
耕捞。本來(lái)這是一個(gè)早已失效的報(bào)文段衔掸。但server
收到此失效的連接請(qǐng)求報(bào)文段后,就誤認(rèn)為是client
再次發(fā)出的一個(gè)新的連接請(qǐng)求俺抽。于是就向client
發(fā)出確認(rèn)報(bào)文段敞映,同意建立連接。假設(shè)不采用“三次握手”磷斧,那么只要server
發(fā)出確認(rèn)振愿,新的連接就建立了。由于現(xiàn)在client
并沒(méi)有發(fā)出建立連接的請(qǐng)求弛饭,因此不會(huì)理睬server
的確認(rèn)冕末,也不會(huì)向server
發(fā)送數(shù)據(jù)。但server
卻以為新的運(yùn)輸連接已經(jīng)建立侣颂,并一直等待client
發(fā)來(lái)數(shù)據(jù)档桃。這樣,server
的很多資源就白白浪費(fèi)掉了憔晒。采用“三次握手”的辦法可以防止上述現(xiàn)象發(fā)生藻肄。例如剛才那種情況,client
不會(huì)向server
的確認(rèn)發(fā)出確認(rèn)拒担。server
由于收不到確認(rèn)嘹屯,就知道client
并沒(méi)有要求建立連接〈雍常”
TCP三次握手時(shí)州弟,第三次握手失敗怎么辦?
如果三次都成功的話:
- 客戶端發(fā)出了SYN包給服務(wù)器,客戶端進(jìn)入SYN_SEND狀態(tài)婆翔。
- 服務(wù)器收到SYN包后發(fā)出SYN+ACK數(shù)據(jù)包,服務(wù)器進(jìn)入SYN_RECV狀態(tài)桐经。
- 客戶端收到SYN+ACK后發(fā)出ACK給服務(wù)器,客戶端進(jìn)入ESTABLISH狀態(tài)
- 服務(wù)器收到最后的ACK,服務(wù)器進(jìn)入ESTABLISH狀態(tài)浙滤。
我們了解一下TCP狀態(tài)轉(zhuǎn)換圖和狀態(tài)偽碼可以發(fā)現(xiàn):
case:SYN_RECV狀態(tài)
if(收到ACK報(bào)文)
進(jìn)入ESTABLISH狀態(tài)
if(超時(shí))
發(fā)送RTS報(bào)文阴挣,進(jìn)入CLOSED狀態(tài)
if(收到“關(guān)閉報(bào)文”)
發(fā)送FIN報(bào)文,進(jìn)入FIN_WAIT_1狀態(tài)
if(收到RTS報(bào)文)
進(jìn)入LISTEN狀態(tài)
if(收到其他報(bào)文段或報(bào)文)
發(fā)出差錯(cuò)報(bào)文
break
當(dāng)?shù)谌挝帐质r(shí)的處理操作纺腊,可以看出當(dāng)失敗時(shí)服務(wù)器并不會(huì)重傳ACK報(bào)文畔咧,而是直接發(fā)送RTS報(bào)文段,進(jìn)入CLOSED狀態(tài)揖膜。這樣做的目的是為了防止SYN洪泛攻擊誓沸。
何為SYN洪泛攻擊呢?在TCP連接建立過(guò)程中很容易碰到嚴(yán)重的的安全問(wèn)題壹粟,稱為SYN洪泛攻擊拜隧。當(dāng)一個(gè)或多個(gè)惡意攻擊者向服務(wù)器發(fā)送大量的SYN報(bào)文時(shí),服務(wù)器認(rèn)為不同客戶發(fā)來(lái)了打開(kāi)請(qǐng)求趁仙,于是就回分配資源洪添。然后,服務(wù)器向假冒的客戶發(fā)送SYN+ACK報(bào)文雀费,而這些報(bào)文都丟失了干奢。如果在第三次握手這段很短的時(shí)間內(nèi),服務(wù)器大量資源被占用而沒(méi)有被利用盏袄,服務(wù)器會(huì)因?yàn)橘Y源耗盡而無(wú)法接受合法客戶的連接請(qǐng)求忿峻。這種SYN洪泛攻擊稱為拒絕服務(wù)攻擊的安全攻擊,即攻擊者用大量的請(qǐng)求壟斷一個(gè)系統(tǒng)辕羽,使這個(gè)系統(tǒng)因超載而拒絕為合法的請(qǐng)求提供服務(wù)逛尚。(更多內(nèi)容請(qǐng)參考TCP/IP協(xié)議簇一書(shū))
關(guān)閉連接時(shí)半關(guān)閉選項(xiàng)的四向握手
TCP連接是全雙工的,所以它允許兩個(gè)方向的數(shù)據(jù)傳輸被獨(dú)立關(guān)閉刁愿。換言之绰寞,通信的一端可以發(fā)送結(jié)束報(bào)文段給對(duì)方,告訴它本端已經(jīng)完成了數(shù)據(jù)的發(fā)送酌毡,但允許繼續(xù)接收來(lái)自對(duì)方的數(shù)據(jù)克握,直到對(duì)方也發(fā)送結(jié)束報(bào)文段以關(guān)閉連接蕾管。TCP連接的這種狀態(tài)稱為半關(guān)閉(half close
)狀態(tài)枷踏。
當(dāng)客戶端向服務(wù)器發(fā)送一個(gè)FIN報(bào)文段后,此連接被半關(guān)閉了掰曾。服務(wù)器發(fā)送ACK報(bào)文段來(lái)接受這個(gè)半關(guān)閉旭蠕。但是服務(wù)器仍然可以發(fā)送數(shù)據(jù),當(dāng)服務(wù)器把處理完的數(shù)據(jù)都發(fā)送完畢之后,發(fā)送FIN報(bào)文段(半關(guān)閉)掏熬,并且被客戶發(fā)來(lái)的ACK予以確認(rèn)(關(guān)閉)佑稠。
當(dāng)關(guān)閉連接時(shí)最后一個(gè)ACK丟失怎么辦?
如果最后一個(gè)ACK丟失的話旗芬,TCP就會(huì)認(rèn)為它的FIN丟失舌胶,進(jìn)行重發(fā)FIN。在客戶端收到FIN后疮丛,就會(huì)設(shè)置一個(gè)2MSL計(jì)時(shí)器幔嫂,2MSL計(jì)時(shí)器可以使客戶等待足夠長(zhǎng)的時(shí)間,使得在ACK丟失的情況下誊薄,可以等到下一個(gè)FIN的到來(lái)履恩。如果在TIME-WAIT狀態(tài)匯總有一個(gè)新的FIN到達(dá)了,客戶就會(huì)發(fā)送一個(gè)新的ACK呢蔫,并重新設(shè)置2MSL計(jì)時(shí)器切心。
示例圖解如下:
如果重傳FIN到達(dá)客戶端時(shí),客戶端已經(jīng)進(jìn)入CLOSED狀態(tài)時(shí)片吊,那么客戶就永遠(yuǎn)收不到這個(gè)重傳的FIN報(bào)文段绽昏,服務(wù)器收不到ACK,服務(wù)器無(wú)法關(guān)閉連接俏脊。
但是服務(wù)器并不會(huì)一直無(wú)法關(guān)閉而涉,服務(wù)器會(huì)進(jìn)行不斷的探查,會(huì)發(fā)送十個(gè)間隔為75秒的探查联予,如果探查都沒(méi)有收到回應(yīng)啼县,則認(rèn)為客戶端已經(jīng)關(guān)閉,服務(wù)器也將關(guān)閉沸久,終止鏈接季眷。