TCP 可靠傳輸機制詳解

目錄

TCP協議的特點

TCP 報文段

TCP "三次握手"

TCP "四次揮手"

客戶端和服務器端所經歷的狀態

TCP 可靠傳輸

TCP流量控制

TCP擁塞控制

面試相關問題


前言

本篇博文主要是爲了複習 TCP 協議而做的總結。其中很多內容都是來自於《計算機網絡》,《Linux網絡編程》,《TCP/IP詳解》等書籍。首先可以從 TCP 協議思維導圖看到本文的大致內容。雖然有很多內容,但"三次握手"、"四次揮手"和可靠傳輸機制纔是本文的重點,其中會用 wireshark 抓取相應的包用於協議分析。最後也會總結一些面試常問的問題。TCP 協議思維導圖如下:

 

 

TCP協議的特點

TCP 是在不可靠的 IP 層之上實現的可靠的數據傳輸協議,它主要解決傳輸的可靠、有序、無丟失和不重複的問題。TCP 的主要特點有:

(1) TCP 是面向連接的傳輸層協議。

(2) 每一條 TCP 連接只能有兩個端點,每一條 TCP 連接只能是點對點的(一對一)。

(3) TCP 提供可靠的交付服務,保證傳送的數據無差錯、不丟失、不重複且有序。

(4) TCP 提供全雙工通信,TCP 允許通信雙方的應用進程在任何時候都能發送數據,爲此 TCP 連接的兩端都設有發送緩存和接收緩存,用來臨時存放雙向通信的數據。

發送緩存用來暫存以下數據:①應用程序傳送給發送方 TCP 準備發送的數據;② TCP 已發送出但尚未收到的確認的數據。

接收緩存用來暫存以下數據:①按序到達的但尚未被接收應用程序讀取的數據;②不按序到達的數據。

(5) TCP 是面向字節流的,雖然應用程序和 TCP 的交互是一次一個數據塊,但 TCP 把應用程序交下來的數據看成僅僅是一連串的無結構的字節流。

 

TCP 報文段

TCP 傳輸的數據單元稱爲報文段。一個 TCP 報文段分爲 TCP 首部和 TCP 數據兩部分,整個 TCP 段作爲 IP 數據報的數據部分封裝在 IP 數據報中。

各字段的含義如下:

(1) 源端口和目的端口:各佔2個字節。端口是運輸層與應用層的服務接口。運輸層的複用和分用功能都要通過端口才能實現。

(2) 序號:佔4個字節。TCP是面向字節流的,所以TCP連接中傳送的數據流中的每一個字節都編上一個序號。序號字段的值則指的是本報文段所發送的數據的第一個字節的序號。例如,一個報文段的序號字段值是200,攜帶的數據總共有100字節,表明這個報文段的數據的最後一個字節的序號是299,所以下一個報文段的數據序號應從300開始。

(3) 確認號:佔4個字節,是期望收到對方的下一個報文段的數據的第一個字節的序號。若確認號爲N,表明前N-1的所有數據都已經正確接收。例如,B正確的收到了A發送過來的一個報文段,其序號字段是指501,而數據長度是200字節(序號501~700),表明B正確的收到了A發送的序號700之前的數據。因此B希望收到A的下一個數據序號是701,所以B在發送給A的確認報文段中應把確認號設置成701。

(4) 數據偏移量:佔4位,這裏不是IP數據報分片的那個數據偏移,而是表示首部長度,它指出TCP報文段的數據起始處距離TCP報文段的起始處有多遠。該字段若爲15,則表明TCP首部達到最大長度60字節(以4字節爲計算單位乘以15)。

(5) 保留:佔6位,目前不使用,所以置爲0。

(6) 緊急位 URG:當其值爲1時,表明緊急指針字段有效。它會告訴系統此報文段中有緊急數據,應該儘快傳送。但是URG需要和緊急指針配套使用,即從第一個字節到緊急指針所指字節就是緊急數據。

(7) 確認位 ACK:只有當ACK=1時,確認號字段纔有效。TCP規定,在連接建立後所有傳送的報文段都必須把ACK置爲1。

(8) 推送位 PSH:TCP收到PSH=1的報文段,就儘快的交付接收應用進程,而不再等到整個緩存都填滿了後再向上交付。

(9) 復位位 RST:當RST=1時,表明TCP連接中出現了嚴重的錯誤,必須釋放連接,然後再重新建立運輸連接。

(10) 同步位 SYNSYN=1表示這是一個連接請求或連接接收報文。當SYN=1,ACK=0時,表明這是一個連接請求報文,對方若同意建立連接,則在響應報文中使用SYN=1,ACK=1。

(11) 終止位 FIN:用來釋放一個連接。FIN=1表明此報文段的發送方的數據已經發送完畢,並要求釋放傳輸連接。

(12) 窗口:佔2個字節。它指出現在允許對方發送的數據量,接收方的數據緩存空間是有限的,所以使用窗口值作爲接收方讓發送方設置其發送窗口的依據,單位是字節。例如,設確認號是101,窗口字段是1000.這就表明,從101號開始,發送此報文段的一方還有接收1000字節數據(字節序號是101~1100)的接收緩存空間。

(13) 校驗和:佔2個字節。它的檢驗範圍包括首部和數據部分。計算時要在TCP報文段前面加上12字節的僞首部。

(14) 緊急指針:佔16位,指出在本報文段中緊急數據共有多少個字節。

(15) 選項:長度可變。TCP最初只規定了一種選項,即最大報文段長度(MSS)。

(16) 填充:這是爲了使整個首部長度是4字節的整數倍。

 

主要需要理解以下幾個字段:

源端口號和目標端口號:誰發的和發給誰(類似於哲學問題:你是誰?從哪裏來?到哪裏去?)

序號:爲了解決亂序問題;

確認序號:發出去的包應該有確認,沒有收到就應該重新發送,直到送達;

狀態位:常見的有SYN、ACK、FIN,分別表示發起一個連接、確認和結束連接;

窗口大小:用於TCP流量控制,通信雙方各聲明一個窗口,說明自己當前的處理能力發送的太快,處理不了,發的太慢,影響發送的效率

 

TCP "三次握手"

第一次握手:客戶端首先向服務器發送一個連接請求報文段。這個特殊的報文段中不含應用層數據,其首部中的SYN標誌位被置爲1。另外,客戶端會隨機選擇一個起始號seq=x(連接請求報文不攜帶數據,但要消耗掉一個序號)。客戶端進程進入SYN_SENT(同步發送)狀態。 

第二次握手:服務器收到連接請求報文段後,如同意建立連接,就向客戶端發回確認,併爲該TCP連接分配TCP緩存和變量。在確認報文段中,SYN和ACK位都被置爲1,確認號字段的值爲x+1,並且服務器隨機產生起始序號seq=y(確認報文不攜帶數據,但也要消耗掉一個序號)。確認報文段同樣不包含應用層數據。服務端進入SYN_RCVD(同步接收)狀態。

第三次握手:當客戶端收到確認報文段後,還要向服務器給出確認,並且也要給該連接分配緩存和變量。這個報文段的ACK標誌位被置爲1,序號字段爲x+1,確認號字段爲ack=y+1。該報文可以攜帶數據,如果不攜帶數據則不消耗序號。客戶端進入ESTABLISHED(建立連接)狀態。

在成功的完成以上三步後,TCP連接就建立了,接下來就可以傳送應用層數據了。TCP提供的是全雙工通信,因此通信雙方的應用進程在任何時候都能發送數據。注意:服務器端的資源是在完成第二次握手時分配的,而客戶端的資源是在完成第三次握手時分配的。所以服務器易於受到SYN洪泛攻擊。

 

TCP "三次握手"報文詳細分析

TCP報文:[29 05 01 bb f4 0a 2c 77 1f a9 79 d2 50 10 01 00 b9 84 00 00]
爲了更易理解:[29 05 01 bb]
            [f4 0a 2c 77]
            [1f a9 79 d2]
            [50 10 01 00]
            [b9 84 00 00]
源端口:2905(HEX) = 10501(DEC)
目的端口:01bb(HEX) = 443(DEC)
序號:1       爲什麼連接過程中的"序號"顯示1,而相應字段的值不是1。詳情請看面試問題的(6)
確認號:1
十六進制[50 10]轉化成二進制[0101 0000 0001 0000]
數據偏移(佔4位):單位是4個字節,此字段值爲5,所以TCP首部的長度是20字節。
保留(佔6位):保留爲今後使用,目前置爲0。
緊急位URG:0
確認位ACK:1
推送位PSH:0
復位位RST:0
同步位SYN:0
終止位FIN:0
窗口:0100(HEX) = 256(DEC)
檢驗和:0xb984
緊急指針:0

 

TCP "四次揮手"

參與TCP連接的兩個進程中的任何一個都能終止該連接。

第一步:若客戶端打算關閉連接,就向其服務器發送一個連接釋放報文段,並停止再發送數據,主動關閉TCP連接,該報文段的FIN標誌位被置爲1,seq=u,它等於前面已傳送過的數據的最後一個字節的序號加1(FIN報文段即使不攜帶數據,也要消耗掉一個序號)。TCP是全雙工的,即可以想象成是一條TCP連接上有兩條數據通路。當發送FIN報文時,發送FIN的一端就不能再發送數據,也就是關閉了其中一條數據通路,但另一條沒有關閉的數據通路仍可以發送數據。

第二步:服務器收到連接釋放報文段後就會發出確認,確認號爲ack=u+1,而這個報文段自己的序號是v,等於它前面已傳送過的數據的最後一個字節的序號加1。此時,從客戶端到服務器這個方向的連接就釋放了,TCP連接處於半關閉狀態。但服務器若發送數據,客戶端仍能接收,即從服務器到客戶端這個方向的連接並未關閉。

第三步:若服務器已經沒有要向客戶端發送的數據,就釋放連接,此時發出FIN=1的連接釋放報文段。

第四步:客戶端收到連接釋放報文段後,必鬚髮出確認。在確認報文段中,ACK字段被置爲1,確認號ack=w+1,序號seq=u+1。此時TCP連接還沒有釋放掉,必須經過2MSL後,客戶端才進入連接關閉狀態。

 

客戶端和服務器端所經歷的狀態

結合連接和釋放過程圖看狀態轉換圖,以下是客戶端的狀態轉換圖:

服務器端的狀態轉換圖:

完整的狀態轉換圖(紅線是客戶端的過程,藍線是服務器的過程,表示正常的連接和斷開過程):

斷開過程中的狀態解析

(1)FIN_WAIT_1: FIN_WAIT_1和FIN_WAIT_2狀態的真正含義都是表示等待對方的FIN報文。而這兩種狀態的區別是: FIN_WAIT_1狀態實際上是當SOCKET在ESTABLISHED狀態時,它想主動關閉連接,向對方發送了FIN報文,此時該SOCKET即進入到FIN_WAIT_1狀態。而當對方迴應ACK報文後,則進入到FIN_WAIT_2狀態,當然在實際的正常情況下,無論對方何種情況下,都應該馬上回應ACK報文,所以FIN_WAIT_1狀態一般是比較難見到的,而FIN_WAIT_2狀態還有時常常可以用netstat看到。(主動方)

(2)FIN_WAIT_2:上面已經詳細解釋了這種狀態,實際上FIN_WAIT_2狀態下的SOCKET,表示半連接,也即有一方要求close連接,但另外一方告訴對方,我暫時還有點數據需要傳送給你(ACK信息),稍後再關閉連接。(主動方)

(3)TIME_WAIT: 表示收到了對方的FIN報文,併發送出了ACK報文,就等2MSL後即可回到CLOSED可用狀態了。如果FIN_WAIT_1狀態下,收到了對方同時帶FIN標誌和ACK標誌的報文時,可以直接進入到TIME_WAIT狀態,而無須經過FIN_WAIT_2狀態。(主動方)

(4)CLOSING(比較少見): 這種狀態比較特殊,實際情況中應該是很少見,屬於一種比較罕見的例外狀態。正常情況下,當你發送FIN報文後,按理來說是應該先收到(或同時收到)對方的 ACK報文,再收到對方的FIN報文。但是CLOSING狀態表示你發送FIN報文後,並沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。什麼情況下會出現此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方几乎在同時close一個SOCKET的話,那麼就出現了雙方同時發送FIN報文的情況,也即會出現CLOSING狀態,表示雙方都正在關閉SOCKET連接。

(5)CLOSE_WAIT: 你需要查看你是否還有數據發送給對方,如果沒有的話,那麼你也就可以close這個SOCKET,發送FIN報文給對方,即關閉連接。所以你在CLOSE_WAIT狀態下,需要完成的事情是等待你去關閉連接。(被動方)

(6)LAST_ACK: 它是被動關閉一方在發送FIN報文後,最後等待對方的ACK報文。當收到ACK報文後,也即可以進入到CLOSED可用狀態了。(被動方)

(7)CLOSED: 表示連接中斷。

 

斷開連接時的意外

當TCP連接發生一些物理上的意外情況時,例如網線斷開,linux上的TCP實現會依然認爲該連接有效,而windows則會在一定時間後返回錯誤信息。這似乎可以通過設置SO_KEEPALIVE選項來解決,不過不知道這個選項是否對於所有平臺都有效。

 

TIME_WAIT狀態所帶來的影響

當某個連接的一端處於TIME_WAIT狀態時,該連接將不能再被使用。事實上,對於我們比較有現實意義的是,這個端口將不能再被使用。某個端口處於TIME_WAIT狀態(其實應該是這個連接)時,這意味着這個TCP連接並沒有斷開(完全斷開),那麼,如果你bind這個端口,就會失敗。對於服務器而言,如果服務器突然crash掉了,那麼它將無法再2MSL內重新啓動,因爲bind會失敗。解決這個問題的一個方法就是設置socket的SO_REUSEADDR選項。這個選項意味着你可以重用一個地址。

注意:當建立一個TCP連接時,服務器端會繼續用原有端口監聽,同時用這個端口與客戶端通信。而客戶端默認情況下會使用一個隨機端口與服務器端的監聽端口通信。有時候,爲了服務器端的安全性,我們需要對客戶端進行驗證,即限定某個IP某個特定端口的客戶端。客戶端可以使用bind來使用特定的端口。對於服務器端,當設置了SO_REUSEADDR選項時,它可以在2MSL內啓動並listen成功。 但是對於客戶端,當使用bind並設置SO_REUSEADDR時,如果在2MSL內啓動,雖然bind會成功,但是在windows平臺上connect會失敗。而在linux上則不存在這個問題。

 

TCP 可靠傳輸

TCP的任務是在IP層的不可靠、盡力而爲服務的基礎上建立一種可靠數據傳輸服務。TCP提供的可靠數據傳輸服務就是要保證接收方進程從緩衝區讀出的字節流與發送方發出的字節流是完全一樣的。TCP使用了校驗、序號、確認和重傳機制來達到這個目的。

校驗

在計算校驗和時,要在TCP數據報之前增加12個字節的僞首部,僞首部並不是TCP報文段真正的首部。只是在計算校驗和時,臨時添加在TCP數據報文段的前面,得到一個臨時的TCP報文段。僞首部既不向下傳送也不向上遞交,而僅僅是爲了計算校驗和。注意:IP數據報的校驗和只檢驗IP數據報的首部,但TCP的校驗和會把首部和數據部分一起檢驗。

序號

TCP首部的序號字段用來保證數據能有序提交給應用層,TCP把數據看成一個無結構但是有序的字節流,而序號是建立在傳送的字節流之上,而不是建立在報文段之上。TCP連接中傳送的數據流中的每一個字節都編上一個序號。序號字段的值則指的是本報文段所發送的數據的第一個字節的序號。

確認

TCP首部的確認號是期望收到對方的下一個報文段的數據的第一個字節的序號。TCP默認使用累計確認,即TCP只確認數據流中至第一個丟失字節爲止的字節。例如,接收方B收到了發送方A發送的包含字節0~2和6~7的報文段。由於某些原因,B還沒有收到字節3~5的報文段,此時B仍在等待字節3(和其後面的字節),因此,B到A的下一個報文段將確認號字段設置爲3。

重傳

有兩種事件會導致TCP對報文段進行重傳:超時冗餘ACK

(1) 超時

TCP每發送一個報文段,就對這個報文段設置一次計時器。只要計時器設置的重傳時間到期但還沒有收到確認,就要重傳這一報文段。當檢測到接收數據有錯誤時,會採取直接丟棄出錯的數據,發送端等待接收端的確認超時後,會自動重發該報文段。

由於IP數據報在傳輸的時候選擇的路由變化很大,因此傳輸層的往返時延的方差很大。爲了計算超時計時器的重傳時間,TCP採用一種自適應算法,它記錄一個報文段發出的時間,以及收到相應確認的時間,這兩個時間之差叫做報文段的往返時間(RTT)。TCP保留了RTT的一個加權平均往返時間RTTs,當第一次測量RTT樣本時,RTTs值就爲所測量到的RTT樣本的值,但之後每測量一個新的RTT樣本,就按下式重新計算一次RTTs:

新的RTTs = ( 1- a ) * (舊的RTTs) + a(新的RTT樣本)

在上式中0 <= a < 1。若a很接近於零,表示新的RTTs值和舊的RTTs值相比變化不大,而受新的RTT樣本影響不大(RTT值更新較慢)。若a接近於1,則表示新的RTTs值受新的RTT樣本的影響較大(RTT值更新較快)。[RFC 2988]推薦的a值爲0.125。

所以超時計時器設置的超時重傳時間(RTO)應略大於上面得出的加權平均往返時間RTTs。即RTO = RTTs + 4RTTd。其中RTTd是RTT的偏差的加權平均值,它與RTTs和新的RTT樣本之差有關。當第一次測量時,RTTd取爲測量到的RTT樣本值的一半,以後測量中,使用下式計算:新的RTTd = (1-β) *(舊的RTTd) + β*|RTTs - 新的RTT樣本|,其中β是個小於1的係數,它的推薦值是0.25。

 

(2) 冗餘ACK

超時觸發重傳存在的一個問題就是超時週期往往太長。幸運的是,發送方通常可在超時事件發生之前通過注意冗餘ACK來較好地檢測丟包情況。冗餘ACK就是再次確認某個報文段的ACK,而發送方先前已經收到過該報文段的確認。例如,發送方A發送了序號爲1、2、3、4、5的TCP報文段,其中2號報文段丟失了,它將無法到達接收方B。因此,3、4、5號報文段對於B來說就成了失序報文段。TCP規定每當比期望序號大的失序報文段到達時,發送冗餘ACK,指明下一個期望字節的序號[RFC 1122, RFC 2581]。在這個例子中,3、4、5號報文到達B,但它們不是B所期待的下一個報文,於是B就發送3個對1號報文段的冗餘ACK,表示自己期望接收2號報文段。TCP規定當發送方收到對同一個報文段的3個冗餘ACK時,就可以認爲跟在這個被確認報文段之後的報文段已經丟失。就前面的例子而言,當A收到對於1號報文段的3個冗餘ACK時,則它可以認爲2號報文段已經丟失。這時發送方A可以立即對2號報文執行重傳,這種技術成爲快速重傳

 

TCP流量控制

TCP提供了流量控制服務以消除發送方使接收方緩存區溢出的可能性,因此TCP流量控制是爲了匹配發送方的發送速率與接收方的讀取速率。TCP提供一種基於滑動窗口協議的流量控制機制。其原理是:在通信過程中,接收方根據自己接收緩存的大小,動態地調整發送方的發送窗口大小,這就是接收窗口rwnd,即調整TCP報文段首部中的"窗口"字段的值,來限制發送方向網絡注入報文的速率。同時,發送方根據其對當前網絡擁塞程度的估計而確定窗口值,稱爲擁塞窗口cwnd,其大小與網絡的帶寬和時延密切相關。

例如,在通信中,有效數據只從A發往B,而B僅向A發送確認報文,這時,B就可以通過設置確認報文段首部的窗口字段來將rwnd值來限制自己發送窗口的大小,這樣可以將未確認的數據量控制再rwnd大小之內,保證了A不會使B得接收緩存溢出。A的發送窗口的實際大小取的是rwnd和cwnd中的最小值。

設主機A向主機B發送數據,在連接建立時,B告訴A:"我的接收窗口rwnd = 400(字節)"。

傳輸層和數據鏈路層的流量控制的區別在於:傳輸層定義了端到端用戶之間的流量控制,數據鏈路層定義了兩個中間的相鄰結點的流量控制。另外,數據鏈路層滑動窗口協議的窗口大小不能動態變化,傳輸層則可以動態變化。

 

TCP擁塞控制

所謂的擁塞控制就是爲了防止過多的數據注入網絡中,這樣可以使網絡中的路由器或鏈路不會過載。當出現擁塞時,端點並不能瞭解到擁塞發生的細節,對通信連接的端點來說,擁塞往往表現爲通信時延的增加。擁塞控制和流量控制相似的地方是通過控制發送方發送數據的速率來達到效果。

擁塞控制與流量控制的區別:擁塞控制是讓網絡能夠承受現有的網絡負荷,它是一個全局性的過程,涉及所有的主機、所有的路由器,以及與降低網絡傳輸性能有關的所有因素。而流量控制往往使指點對點通信量的控制,即接收端控制發送端,它所做的就是抑制發送端發送數據的速率,以便使接收端來得及接收。

爲了更好地對傳輸層進行擁塞控制,有以下四種算法:慢開始、擁塞避免、快重傳、快恢復。發送方在確定發送報文段的速率時,既要根據接收方的接收能力,又要從全局考慮不要使網絡發生擁塞。因此,TCP協議要求發送方維護以下兩個窗口:

(1)接收窗口rwnd,接收方根據目前接收緩存大小所許諾的最新的窗口值,反映了接收方的容量。有接收方根據其放在TCP報文的首部的"窗口"字段通知發送方。

(2)擁塞窗口cwnd,發送方根據自己估算的網絡擁塞程度而設置的窗口值,反映了網絡當前容量。只要網絡沒有出現擁塞,擁塞窗口就再增大一些,以便把更多的分組發送出去。但只要網絡出現擁塞,擁塞窗口就減少一些,以減少注入網絡中的分組數。發送窗口的上限值應當取接收窗口rwnd和擁塞窗口cwnd中較小的一個,即:Min[rwnd, cwnd]。

 

慢開始和擁塞避免

(1)慢開始算法

在TCP剛剛連接好,開始發送TCP報文段時,先讓擁塞窗口cwnd=1,即一個最大報文段長度MSS。而在每收到一個對新的報文段的確認後,將cwnd加倍,即剛開始會增大一個MSS。用這樣的方法逐步增大發送方的擁塞窗口cwnd,可以使分組注入到網絡的速率更加合理。例如,A向B發送數據,當發送時A的擁塞窗口爲2,那麼A一次可以發送兩個TCP報文段,當經過一個RTT後,A收到B對剛纔兩個報文的確認,於是就把擁塞窗口調整爲4,即下一次發送時就可以發送4個報文段。

使用慢開始算法後,每經過一個傳輸輪次,擁塞窗口cwnd就會加倍,即cwnd的大小呈指數形式增長。這樣慢開始一直把擁塞窗口cwnd增大到一個規定的慢開始門限ssthresh(閾值),然後改用擁塞避免算法。

 

(2)擁塞避免算法

擁塞避免算法的做法是:發送端的擁塞窗口cwnd每經過一個往返時延RTT就增加一個MSS的大小,而不是加倍,使cwnd按線性規律緩慢增長(即加法增大),而當出現一次超時(網絡擁塞)時,會令慢開始門限ssthresh等於當前cwnd的一半(即乘法減小)。

根據cwnd的大小執行不同的算法,可歸納爲:

當cwnd < ssthresh時,使用慢開始算法。

當cwnd > ssthresh時,改用擁塞避免算法。

當cwnd = ssthresh時,既可以使用慢開始也可以使用擁塞避免算法。

 

(3)網絡擁塞的處理

當網絡出現擁塞時,無論是在慢開始階段還是在擁塞避免階段,只要發送方檢測到超時事件的發生(沒有按時收到確認,重傳計時器超時),就把慢開始門限ssthresh設置爲出現擁塞時的發送窗口cwnd值的一半(但不能小於2)。然後把擁塞窗口cwnd重新設置爲1,執行慢開始算法。這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生擁塞的路由器有足夠的時間把隊列中積壓的分組處理完。擁塞避免是指在擁塞避免階段把擁塞窗口控制爲線性規律增長,使網絡比較不容易出現擁塞,利用以上措施想要完全避免網絡擁塞是不可能的。

1.初始時,擁塞窗口置爲1,即cwnd=1,慢開始門限置爲16,即ssthresh=16。慢開始階段,cwnd初值爲1,以後發送方每收到一個確認ACK,cwnd值加倍,即經過每個傳輸輪次(RTT),cwnd呈指數規律增長。

2.當擁塞窗口cwnd增長到慢開始門限ssthresh時(即當cwnd=16時),就改用擁塞避免算法,cwnd按線性規律增長。

3.若此時cwnd=24時,網絡發生擁塞,更新ssthresh值爲12(即變爲超時時cwnd值的一半),cwnd重置爲1,並執行慢開始算法,當cwnd=12時,改爲擁塞避免算法。

注意,在慢開始階段,若2*cwnd > ssthresh,則下一個RTT的cwnd應等於ssthresh,而不是2*cwnd,即cwnd不能越過ssthresh值。如上圖,在16個輪次時,cwnd=8、ssthresh=12,第17輪次時,cwnd=12,而不是16。

 

快重傳和快恢復

(1)快重傳

TCP可靠傳輸機制中,快速重傳技術使用了冗餘ACK來檢測丟包的發生。同樣,冗餘ACK也用於網絡擁塞的檢測。快重傳並非取消重傳計時器,而是在某些情況下可更早的重傳丟失的報文段。當發送方連續收到三個重複的ACK報文時,直接重傳對方尚未收到的報文段,而不必等待那個報文段設置的重傳計時器超時。

如果收到3個相同的ACK。TCP在收到亂序到達包時就會立即發送ACK,TCP利用3個相同的ACK來判定數據包的丟失,此時進行快速重傳,快速重傳做的事情有:

1.把ssthresh設置爲cwnd的一半。

2.把cwnd再設置爲ssthresh的值(具體實現有些爲ssthresh+3)。

3.重新進入擁塞避免階段。

 

(2)快恢復

後來的"快速恢復"算法是在上述的"快速重傳"算法後添加的,當收到3個重複ACK時,TCP最後進入的不是擁塞避免階段,而是快速恢復階段。快速重傳和快速恢復算法一般同時使用。快速恢復的思想是"數據包守恆"原則,即同一個時刻在網絡中的數據包數量是恆定的,只有當"老"數據包離開了網絡後,才能向網絡中發送一個"新"的數據包,如果發送方收到一個重複的ACK,那麼根據TCP的ACK機制就表明有一個數據包離開了網絡,於是cwnd加1。如果能夠嚴格按照該原則那麼網絡中很少會發生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。

具體來說快速恢復的主要步驟是:

1.當收到3個重複ACK時,把ssthresh設置爲cwnd的一半,把cwnd設置爲ssthresh的值加3(有的實現版本不加3),然後重傳丟失的報文段,加3的原因是因爲收到3個重複的ACK,表明有3個"老"的數據包離開了網絡。

2.再收到重複的ACK時,擁塞窗口增加1。

3.當收到新的數據包的ACK時,把cwnd設置爲第一步中的ssthresh的值。原因是因爲該ACK確認了新的數據,說明從重複ACK時的數據都已收到,該恢復過程已經結束,可以回到恢復之前的狀態了,也即再次進入擁塞避免狀態。

快速重傳算法首次出現在4.3BSD的Tahoe版本,快速恢復首次出現在4.3BSD的Reno版本,也稱之爲Reno版的TCP擁塞控制算法。

在流量控制中,發送方發送數據的量由接收方決定,而在擁塞控制中,有發送方自己通過檢測網絡狀況而決定。實際上,慢開始、擁塞避免算法、快重傳和快恢復集中算法應該是同時應用在擁塞控制機制之中的,當發送方檢測到超時的時候就採用慢開始和擁塞避免,當發送方接收到冗餘ACK的時候就採用快重傳和快恢復。

 

面試相關問題

(1)爲什麼四次揮手發送最後一次報文後要等待2MSL (報文最大生存時間)的時間?

答:(1)爲了保證A發送的最後一個確認報文段能夠到達B。如果A不等待2MSL,若A返回的最後確認報文段丟失,則B不能進入正常關閉狀態,而A此時已經關閉,也不可能再重傳。

(2)防止出現”已失效的連接請求報文段”。A在發送完最後一個確認報文段後,再經過2MSL可保證本連接持續的時間內所產生的所有報文段從網絡中消失。

 

(2)TCP使用的是GBN(後退N幀協議)還是SR(選擇重傳協議)?

答:這是爲了人踩坑而出的問題。因爲TCP使用累計確認,看起來像是GBN。但是,正確收到但失序的報文並不會被丟棄,而是緩存起來,並且發送冗餘ACK指明希望收到的下一個報文段,這是TCP方式和GBN的顯著區別。例如,A發送了N個報文段,其中第k(k < N)個報文段丟失,其餘N-1個報文段正確地按序到達接收方B。當使用GBN時,A需要重傳分組k,以及所有後繼分組k+1,k+2,...,N。相反,TCP卻最多重傳一個報文段,即報文段k。另外,TCP中提供一個SACK(Selective ACK)選項,也就是選擇確認選項。當使用選擇確認選項的時候,TCP看起來就和SR非常相似。因此,TCP的差錯恢復機制可以看成是GBN和SR協議的混合體。

 

(3)爲什麼超時時間發生時cwnd被置爲1,而收到3個冗餘ACK時cwnd只是減半?

答:首先應分析那種情況的網絡擁塞程度更嚴重。其實不難發現,在收到3個冗餘ACK的情況下,網絡雖然擁塞,但至少ACK報文段能夠被正確交付。而當超時發生時,說明網絡可能已經擁塞的連ACK報文段都傳輸不了了,發送方只能等待超時後重傳數據。因此,超時時間發生時,網絡擁塞更嚴重,所以發送方應該最大限度地抑制數據發送量,所以cwnd置爲1;收到3個冗餘ACK時,網絡擁塞相對而言不是很嚴重,所以cwnd減半即可。

 

(4)爲什麼不採用"兩次握手"建立連接?

答:這主要是爲了防止兩次握手情況下已失效的連接請求報文段突然有傳送到服務端,而產生了錯誤。考慮以下情況:客戶A想服務器B發送TCP連接請求,第一個連接請求報文在網絡的某個結點長時間滯留,A超時後認爲報文丟失,於是再重傳一次連接請求,B收到後建立連接。數據傳輸完畢後雙方斷開連接。此時,前一個滯留在網絡中的連接請求到達了服務端B,而B認爲A有發來連接請求,此時若是使用"三次握手",則B向A返回確認報文段,由於是一個失效的請求,因此A不予理睬,建立連接失敗。若採用的是"兩次握手",則這種情況下B認爲傳輸連接已經建立,並一直等待A傳輸數據,而A此時並無連接請求,因此不予理睬,這樣就造成了B的資源白白浪費了。

 

(5)是否TCP和UDP都需要計算往返時間RTT?

答:往返時間RTT只是針對傳輸層TCP協議才很重要,因爲TCP要根據RTT的值來設置超時計時器的超時時間。UDP沒有確認和重傳機制,因此RTT對UDP沒有什麼意義。

 

(6)爲什麼TCP在建立連接的時候不能每次選擇相同的、固定的初始序號?

答:(1)假如A和B頻繁地建立連接,傳送一些TCP報文段後再釋放連接,然後又不斷的建立新的連接、傳送報文段和釋放連接。

(2)假如每一次建立連接時,主機A都選擇相同的、固定的初始序號,如1。

(3)若主機A發送出的某些TCP報文段在網絡中會滯留較長的時間,以致造成主機A超時重傳這些TCP報文段。

(4)若有一些在網絡中滯留時間較長的TCP報文段最後終於到達了主機B,但這時傳送該報文段的那個連接早已釋放了,而在到達主機B時的TCP連接是一條新的TCP連接。

以上這些情況可能會導致在新的TCP連接中的主機B有可能會接收在舊的連接傳送的、已經沒有意義的、過時的TCP報文段(因爲這個TCP報文段的序號有可能正好處於新的連接所使用的序號範圍內)。因爲必須使得遲到的TCP報文段的序號不在新的連接中使用的序號範圍內。所以,TCP在建立新的連接時所選擇的初始序號一定要和前面的一些連接所使用過的序號不一樣。因此,不同的TCP連接不能使用相同的初始序號。

 

(7)在使用TCP傳輸數據時,如果有一個確認報文段丟失了,也不一定會引起與該確認報文段對應的數據的重傳。試說明理由。

答:這是因爲發送方可能還未重傳時,就收到了更高序號的確認。例如主機A連續發送兩個報文段,均正確到達主機B。B連續發送兩個確認ACK1和ACK2(ACK2的序號比ACK1的序號高)。但前一個確認幀在傳輸時丟失了。若在超時前,ACK2被A接收,更高的序號代表該序號之前的所有字節都被接收了,所以A知道前一個報文也被正確的接收了,這種情況下A不會重傳第一個報文段。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章