面試必備之TCP常見知識點整理


難免有遺漏,希望大佬們在評論中指出還有那些需要添加的點。感謝!感謝!感謝!


一、TCP首部結構詳解


TCP數據封裝在一個IP數據報中:



640?wx_fmt=jpeg

TCP數據在IP數據包中的封裝

(圖片來自《TCP/IP詳解卷1》)


下圖是TCP報文數據格式。TCP首部如果不計選項和填充字段,它通常是20個字節


640?wx_fmt=jpeg

TCP報文格式


下面分別對其中的字段進行介紹:


源端口和目的端口:各佔2個字節,這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連接。有時一個IP地址和一個端口號也稱爲socket(插口)。


序號:佔4個字節,是本報文段所發送的數據項目組第一個字節的序號。在TCP傳送的數據流中,每一個字節都有一個序號。例如,一報文段的序號爲300,而且數據共100字節,則下一個報文段的序號就是400;序號是32bit的無符號數,序號到達2^32-1後從0開始。(注:如何防止從0開始後序號相同的問題)


確認序號:佔4字節,是期望收到對方下次發送的數據的第一個字節的序號,也就是期望收到的下一個報文段的首部中的序號;確認序號應該是上次已成功收到數據字節序號+1。只有ACK標誌爲1時,確認序號纔有效。


數據偏移:佔4比特,表示數據開始的地方離TCP段的起始處有多遠。實際上就是TCP段首部的長度。由於首部長度不固定,因此數據偏移字段是必要的。數據偏移以32位爲長度單位,也就是4個字節,因此TCP首部的最大長度是60個字節。即偏移最大爲15個長度單位=1532位=154字節。

保留: 6比特,供以後應用,現在置爲0。


6個標誌位比特:


URG:當URG=1時,註解此報文應儘快傳送,而不要按本來的列隊次序來傳送。與「緊急指針」字段共同應用,緊急指針指出在本報文段中的緊急數據的最後一個字節的序號,使接管方可以知道緊急數據共有多長;


ACK:只有當ACK=1時,確認序號字段纔有效;


PSH:當PSH=1時,接收方應該儘快將本報文段立即傳送給其應用層。


RST:當RST=1時,表示出現連接錯誤,必須釋放連接,然後再重建傳輸連接。復位比特還用來拒絕一個不法的報文段或拒絕打開一個連接;


SYN:SYN=1,ACK=0時表示請求建立一個連接,攜帶SYN標誌的TCP報文段爲同步報文段;


FIN:發端完成發送任務。


窗口:TCP通過滑動窗口的概念來進行流量控制。設想在發送端發送數據的速度很快而接收端接收速度卻很慢的情況下,爲了保證數據不丟失,顯然需要進行流量控制, 協調好通信雙方的工作節奏。所謂滑動窗口,可以理解成接收端所能提供的緩衝區大小。


TCP利用一個滑動的窗口來告訴發送端對它所發送的數據能提供多大的緩 衝區。窗口大小爲字節數,起始於確認序號字段指明的值(這個值是接收端正期望接收的字節)。窗口大小是一個16bit字段,因而窗口大小最大爲65535字節。


檢驗和:檢驗和覆蓋了整個TCP報文段:TCP首部和數據。這是一個強制性的字段,一定是由發端計算和存儲,並由收端進行驗證。


緊急指針:只有當URG標誌置1時緊急指針纔有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最後一個字節的序號。


二、TCP三次握手過程


640?wx_fmt=jpeg

TCP三次握手

(來源:百度圖片)


客戶端向服務器發出連接請求報文,這時報文首部中的同部位SYN=1,同時選擇一個初始序列號 seq=J ,此時,TCP客戶端進程進入了 SYN-SENT(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。


TCP服務器收到請求報文後,如果同意連接,則發出確認報文。確認報文中應該 ACK=1,SYN=1,確認號是ack=J+1,同時也要爲自己初始化一個序列號 seq=K,此時,TCP服務器進程進入了SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。


TCP客戶進程收到確認後,還要向服務器給出確認。確認報文的ACK=1,ack=K+1,[自己的序列號seq=J+1],此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。


當服務器收到客戶端的確認後也進入ESTABLISHED狀態,此後雙方就可以開始通信了。


三、TCP三次握手問題補充


爲什麼需要三次握手


首先我們要知道信道是不可靠的,但是我們要建立可靠的連接發送可靠的數據,也就是數據傳輸是需要可靠的。在這個時候三次握手是一個理論上的最小值,並不是說是tcp協議要求的,而是爲了滿足在不可靠的信道上傳輸可靠的數據所要求的。


在《計算機網絡》一書中其中有提到,三次握手的目的是「爲了防止已經失效的連接請求報文段突然又傳到服務端,因而產生錯誤」


這種情況是:


一端(client)A發出去的第一個連接請求報文並沒有丟失,而是因爲某些未知的原因在某個網絡節點上發生滯留,導致延遲到連接釋放以後的某個時間纔到達另一端(server)B。


本來這是一個早已失效的報文段,但是B收到此失效的報文之後,會誤認爲是A再次發出的一個新的連接請求,於是B端就向A又發出確認報文,表示同意建立連接。如果不採用「三次握手」,那麼只要B端發出確認報文就會認爲新的連接已經建立了,但是A端並沒有發出建立連接的請求,因此不會去向B端發送數據,B端沒有收到數據就會一直等待,這樣B端就會白白浪費掉很多資源。


如果採用「三次握手」的話就不會出現這種情況,B端收到一個過時失效的報文段之後,向A端發出確認,此時A並沒有要求建立連接,所以就不會向B端發送確認,這個時候B端也能夠知道連接沒有建立。


四、TCP四次揮手過程


640?wx_fmt=jpegTCP四次揮手


1、客戶端進程發出連接釋放報文,並且停止發送數據。釋放數據報文首部,FIN=1,其序列號爲seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加1),此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。


2、服務器收到連接釋放報文,發出確認報文,ACK=1,ack=u+1,並且帶上自己的序列號seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。


3、客戶端收到服務器的確認請求後,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最後的數據)。


4、服務器將最後的數據發送完畢後,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由於在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號爲seq=w,此時,服務器就進入了LAST-ACK(最後確認)狀態,等待客戶端的確認。


5、客戶端收到服務器的連接釋放報文後,必須發出確認,ACK=1,ack=w+1,而自己的序列號是seq=u+1,此時,客戶端就進入了TIME-WAIT(時間等待)狀態。注意此時TCP連接還沒有釋放,必須經過2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入CLOSED狀態。


6、服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB後,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。


五、TCP四次揮手問題補充


爲什麼需要四次揮手


爲了確保數據能夠完成傳輸。


關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之後,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這裏的ACK報文和FIN報文多數情況下都是分開發送的。


釋放連接時爲什麼TIME-WAIT狀態必須等待2MSL時間?


首先說下什麼是MSL:


MSL是Maximum Segment Lifetime英文的縮寫,中文可以譯爲「報文最大生存時間」,他是任何報文在網絡上存在的最長時間,超過這個時間報文將被丟棄。


第一,爲了保證A發送的最後一個ACK報文能夠到達B。這個ACK報文段有可能丟失,因而使處在LAST-ACK狀態的B收不到對已發送的FIN+ACK報文段的確認。B會超時重傳這個FIN+ACK報文段,而A就能在2MSL時間內收到這個重傳的FIN+ACK報文段,重置時間等待計時器(2MSL)。


如果A在TIME-WAIT狀態不等待一段時間,而是在發送完ACK報文段後就立即釋放連接,就無法收到B重傳的FIN+ACK報文段,因而也不會再發送一次確認報文段。這樣,B就無法按照正常的步驟進入CLOSED狀態。


第二,A在發送完ACK報文段後,再經過2MSL時間,就可以使本連接持續的時間所產生的所有報文段都從網絡中消失。這樣就可以使下一個新的連接中不會出現這種舊的連接請求的報文段。


客戶端突然掛掉了怎麼辦?


正常連接時,客戶端突然掛掉了,如果沒有措施處理這種情況,那麼就會出現客戶端和服務器端出現長時期的空閒。解決辦法是在服務器端設置保活計時器,每當服務器收到客戶端的消息,就將計時器復位。超時時間通常設置爲2小時。若服務器超過2小時沒收到客戶的信息,他就發送探測報文段。若發送了10個探測報文段,每一個相隔75秒,還沒有響應就認爲客戶端出了故障,因而終止該連接。


TCP超時重傳


原理是在發送某一個數據以後就開啓一個計時器,在一定時間內如果沒有得到發送的數據報的ACK報文,那麼就重新發送數據,直到發送成功爲止。


影響超時重傳機制協議效率的一個關鍵參數是重傳超時時間(RTO,Retransmission TimeOut)。RTO的值被設置過大過小都會對協議造成不利影響。


(1)RTO設長了,重發就慢,沒有效率,性能差。
(2)RTO設短了,重發的就快,會增加網絡擁塞,導致更多的超時,更多的超時導致更多的重發。


連接往返時間(RTT,Round Trip Time),指發送端從發送TCP包開始到接收它的立即響應所消耗的時間。


RTO理論上最好是網絡 RTT 時間,但又受制於網絡距離與瞬態時延變化,所以實際上使用自適應的動態算法(例如 Jacobson 算法和 Karn 算法等)來確定超時時間。


六、TCP流量控制


TCP流量控制主要是針對接收端的處理速度不如發送端發送速度快的問題,消除發送方使接收方緩存溢出的可能性。


TCP流量控制主要使用滑動窗口協議,滑動窗口是接受數據端使用的窗口大小,用來告訴發送端接收端的緩存大小,以此可以控制發送端發送數據的大小,從而達到流量控制的目的。


接收端通過TCP首部的窗口大小字段反饋當前可接收的字節數。


640?wx_fmt=jpeg

滑動窗口模擬


  • 發送方接收到了對方發來的報文 ack = 33, win = 10,知道對方收到了 33 號前的數據,現在期望接收 [33, 43) 號數據。發送方連續發送了 4 個報文段假設爲 A, B, C, D, 分別攜帶 [33, 35), [35, 36), [36, 38), [38, 41) 號數據。


  • 接收方接收到了報文段 A, C,但是沒收到 B 和 D,也就是隻收到了 [33, 35) 和 [36, 38) 號數據。接收方發送回對報文段 A 的確認:ack = 35, win = 10。


  • 發送方收到了 ack = 35, win = 10,對方期望接收 [35, 45) 號數據。接着發送了一個報文段 E,它攜帶了 [41, 44) 號數據。


  • 接收方接收到了報文段 B: [35, 36), D:[38, 41),接收方發送對 D 的確認:ack = 41, win = 10. (這是一個累積確認)


  • 發送方收到了 ack = 41, win = 10,對方期望接收 [41, 51) 號數據。


    ……


需要注意的是,接收方接收 tcp 報文的順序是不確定的,並非是一定先收到 35 再收到 36,也可能是先收到 36,37,再收到 35.


對於發送端的數據緩衝區有這些量:LastByteSent是目前發送的最後1字節的數據編號;LastByteAckd是目前接收到確認的最後1字節的數據編號;Rcvwin是窗口大小。


鑑於每次發送方都是收到ACK之後滑動窗口繼續發送,發送到LastByteSent這個位置,LastByteSent-LastByteAckd也就是這次發送數據的多少,那麼只要滿足:LastByteSent–LastByteAckd<=RcvWin(接收端空閒窗口大小) 就會保證不會溢出了。


那麼接收端RcvWin怎麼算呢?假設接收端緩衝區大小爲RcvBuffer。


LastByteRead:上層應用程序接收的最後一個字節序號,LastByteRcvd:接收端從網絡接收的最後一個字節序號,那麼LastByteRcvd–LastByteRead就是已經接受但是還沒有傳遞給上層的數據。所以空閒區域RcvWin= RcvBuffer-(LastByteRcvd–LastByteRead).


TCP擁塞控制


TCP發送方可能因爲IP網絡的擁塞而被遏制,TCP擁塞控制就是爲了解決這個問題(注意和TCP流量控制的區別)。


TCP擁塞控制的幾種方法:慢啓動,擁塞避免,快重傳和快恢復。


這裏引入了一個擁塞窗口的概念;


擁塞窗口:發送方維持一個叫做擁塞窗口 cwnd的狀態變量。擁塞窗口的大小取決於網絡的擁塞程度,並且動態變化。發送方的讓自己的發送窗口=min(cwnd,接受端接收窗口大小)。

發送方控制擁塞窗口的原則是:只要網絡沒有出現擁塞,擁塞窗口就增大一些,以便把更多的分組發送出去。但只要網絡出現擁塞,擁塞窗口就減小一些,以減少注入到網絡中的分組數。


下面將討論擁塞窗口cwnd的大小是怎麼變化的。


慢啓動


當主機開始發送數據時,如果立即所大量數據字節注入到網絡,那麼就有可能引起網絡擁塞,因爲現在並不清楚網絡的負荷情況。因此,較好的方法是 先探測一下,即由小到大逐漸增大發送窗口,也就是說,由小到大逐漸增大擁塞窗口數值。


通常在剛剛開始發送報文段時,先把擁塞窗口 cwnd 設置爲一個最大報文段MSS的數值。而在每收到一個對新的報文段的確認後,把擁塞窗口增加至多一個MSS的數值。用這樣的方法逐步增大發送方的擁塞窗口 cwnd ,可以使分組注入到網絡的速率更加合理。


640?wx_fmt=jpeg


每經過一個傳輸輪次,擁塞窗口 cwnd 就加倍。一個傳輸輪次所經歷的時間其實就是往返時間RTT。不過「傳輸輪次」更加強調:把擁塞窗口cwnd所允許發送的報文段都連續發送出去,並收到了對已發送的最後一個字節的確認。


另外,慢開始的「慢」並不是指cwnd的增長速率慢,而是指在TCP開始發送報文段時先設置cwnd=1,使得發送方在開始時只發送一個報文段(目的是試探一下網絡的擁塞情況),然後再逐漸增大cwnd。


爲了防止擁塞窗口cwnd增長過大引起網絡擁塞,還需要設置一個慢開始門限ssthresh狀態變量。慢開始門限ssthresh的用法如下:
當 cwnd < ssthresh 時,使用上述的慢開始算法。
當 cwnd > ssthresh 時,停止使用慢開始算法而改用擁塞避免算法。
當 cwnd = ssthresh 時,既可使用慢開始算法,也可使用擁塞控制避免算法。


擁塞避免


讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。這樣擁塞窗口cwnd按線性規律緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢得多。


無論在慢開始階段還是在擁塞避免階段,只要發送方判斷網絡出現擁塞(其根據就是沒有收到確認),就要把慢開始門限ssthresh設置爲出現擁塞時的發送 方窗口值的一半(但不能小於2)。然後把擁塞窗口cwnd重新設置爲1,執行慢開始算法。這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生 擁塞的路由器有足夠時間把隊列中積壓的分組處理完畢。


如下圖,用具體數值說明了上述擁塞控制的過程。現在發送窗口的大小和擁塞窗口一樣大。


640?wx_fmt=jpeg


讓擁塞窗口cwnd緩慢地增大,即每經過一個往返時間RTT就把發送方的擁塞窗口cwnd加1,而不是加倍。這樣擁塞窗口cwnd按線性規律緩慢增長,比慢開始算法的擁塞窗口增長速率緩慢得多。


無論在慢開始階段還是在擁塞避免階段,只要發送方判斷網絡出現擁塞(其根據就是沒有收到確認),就要把慢開始門限ssthresh設置爲出現擁塞時的發送 方窗口值的一半(但不能小於2)。然後把擁塞窗口cwnd重新設置爲1,執行慢開始算法。這樣做的目的就是要迅速減少主機發送到網絡中的分組數,使得發生 擁塞的路由器有足夠時間把隊列中積壓的分組處理完畢。


如下圖,用具體數值說明了上述擁塞控制的過程。現在發送窗口的大小和擁塞窗口一樣大。


640?wx_fmt=jpeg

慢開始和擁塞避免算法的實現舉例


1、當TCP連接進行初始化時,把擁塞窗口cwnd置爲1。前面已說過,爲了便於理解,圖中的窗口單位不使用字節而使用報文段的個數。慢開始門限的初始值設置爲16個報文段,即 cwnd = 16 。


2、在執行慢開始算法時,擁塞窗口 cwnd 的初始值爲1。以後發送方每收到一個對新報文段的確認ACK,就把擁塞窗口值加1,然後開始下一輪的傳輸(圖中橫座標爲傳輸輪次)。因此擁塞窗口cwnd 隨着傳輸輪次按指數規律增長。當擁塞窗口cwnd增長到慢開始門限值ssthresh時(即當cwnd=16時),就改爲執行擁塞控制算法,擁塞窗口按線 性規律增長。


3、假定擁塞窗口的數值增長到24時,網絡出現超時(這很可能就是網絡發生擁塞了)。更新後的ssthresh值變爲12(即變爲出現超時時的擁塞窗口數值 24的一半),擁塞窗口再重新設置爲1,並執行慢開始算法。當cwnd=ssthresh=12時改爲執行擁塞避免算法,擁塞窗口按線性規律增長,每經過 一個往返時間增加一個MSS的大小。


強調:「擁塞避免」並非指完全能夠避免了擁塞。利用以上的措施要完全避免網絡擁塞還是不可能的。「擁塞避免」是說在擁塞避免階段將擁塞窗口控制爲按線性規律增長,使網絡比較不容易出現擁塞。


快重傳


在超時重傳中,重點是定時器溢出超時了才認爲發送的數據包丟失,快速重傳機制,實現了另外的一種丟包評定標準,即如果我連續收到3次重複ACK,發送方就認爲這個seq的包丟失了,立刻進行重傳,這樣如果接收端回覆及時的話,基本就是在重傳定時器到期之前,提高了重傳的效率。

相關文章
相關標籤/搜索