詳解TCP協議三次握手和四次揮手

TCP建立連接和斷開連接很重要,但是自己又經常忘記一些具體細節,回顧的時候要麼到收藏夾裏找文章,要麼是百度/谷歌一波,偶爾一兩次還好,次數多了略顯麻煩,遂趁這次溫故的機會,記錄下來,方便自己也方便他人~

TCP報文格式

TCP報文格式如下圖所示:
TCP報文格式

其中有幾個字段比較重要,在下面講三次握手和四次揮手是會用到:

  1. 序號:seq序號,佔32位,用來標識從TCP源端向目的端發送的字節流,發起方發送數據時對此進行標記;
  2. 確認序號:ack序號,佔32位,只有ACK標誌位爲1時,確認序號字段纔有效;
  3. 標誌位:共6個,即URGACKPSHRSTSYNFIN,每個標誌位表示一個控制空能,具體如下:
    • URG:緊急指針(urgent pointer),爲1時表示緊急指針有效,爲0則忽略緊急指針。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最後一個字節的序號。TCP的緊急方式是發送端向另一端發送緊急數據的一種方式;
    • ACK:確認序號,爲1時表示確認號有效,爲0表示報文中不含確認信息,忽略確認號字段;
    • PSHPUSH標誌,爲1表示是帶有PUSH標誌的數據,指示接收方在接收到該報文段以後,應儘快將這個報文段交給應用程序,而不是在緩衝區排隊;
    • RST:重置/復位連接,用於重置由於主機崩潰或其他原因而出現錯誤的連接。或者用於拒絕非法的報文段和拒絕連接請求;
    • SYN:同步序號,用於建立連接過程;
    • FINFINISH標誌,釋放連接,爲1時表示發送方已經沒有數據發送了,即關閉本方數據流。

需要特別注意

  1. 確認序號ack與標誌位ACK不同,不能混淆,看下面就會懂;
  2. 確認方的確認序號ack等於發起方的序號seq1,即ack=seq+1,才能夠兩端匹配。

三次握手(建立TCP連接)

三次握手(Three-Way Handshake)即建立TCP連接,指建立一個TCP連接時,需要客戶端和服務端總共發送3個包來確認連接的建立,整個過程大致如下:
TCP三次握手

  1. 第一次握手:Client將標誌位SYN置爲1,隨機產生一個值seq=J,並將該數據包發送給ServerClient進入SYN_SENT狀態,等待Server確認。
  2. 第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求建立連接,Server將標誌位SYNACK都置爲1ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。
  3. 第三次握手:Client收到確認後,檢查ack是否爲J+1ACK是否爲1,如果正確則將標誌位ACK置爲1ack=K+1,並將該數據包發送給ServerServer檢查ack是否爲K+1ACK是否爲1,如果正確則連接建立成功,ClientServer進入ESTABLISHED狀態,完成三次握手,隨後ClientServer之間可以開始傳輸數據了。

半連接(half-open connect)

在三次握手過程中,第二次握手,即Server發送SYN-ACK之後,收到ClientACK之前的TCP連接被稱爲半連接。

此時Server會將第一次握手相關信息放入半連接隊列(syns queue)中,若收到第三次握手Client發送的ACK報文後,則將相關信息挪入全連接隊列(accept queue),否則會有限次數(超時機制)的重傳SYN-ACK。如果半連接隊列或者全連接隊列已滿,則拋棄此次連接。

SYN攻擊(半連接攻擊):

惡意Client在短時間內僞造大量不存在的IP地址,並向Server不斷地發送SYN包,Server回覆確認包,並等待Client的確認,由於源地址是不存在的,因此,Server需要不斷重發直至超時,這些僞造的SYN包將產時間佔用未連接隊列,導致正常的SYN請求因爲隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱瘓。

三次握手的原因

爲了防止已失效的連接請求報文段突然又傳送到了服務端,因而產生錯誤。

舉例:

Client發出的第一個連接請求報文段並沒有丟失,而是在某個網絡結點長時間的滯留了,以致延誤到連接釋放以後的某個時間纔到達Server。本來這是一個早已失效的報文段。但Server收到此失效的連接請求報文段後,就誤認爲是Client再次發出的一個新的連接請求。於是就向Client發出確認報文段,同意建立連接。假設不採用“三次握手”,那麼只要Server發出確認,新的連接就建立了。由於現在Client並沒有發出建立連接的請求,因此不會理睬Server的確認,也不會向Server發送數據。但Server卻以爲新的運輸連接已經建立,並一直等待Client發來數據。這樣,Server的很多資源就白白浪費掉了。採用“三次握手”的辦法可以防止上述現象發生。例如剛纔那種情況,Client不會向Server的確認發出確認。Server由於收不到確認,就知道Client並沒有要求建立連接。

四次揮手(斷開TCP連接)

四次揮手(Four-Way Wavehand)即斷開TCP連接,指斷開一個TCP連接時,需要客戶端和服務端總共發送4個包以確認連接的斷開。整個過程大致如下:
四次揮手

由於TCP連接時全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成數據發送任務後,發送一個FIN來終止這一方向的連接,收到一個FIN只是意味着這一方向上沒有數據流動了,即不會再收到數據了,但是在這個TCP連接上仍然能夠發送數據,直到這一方向也發送了FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉,上圖描述的即是如此。

  1. 第一次揮手:Client發送一個FIN,用來關閉ClientServer的數據傳送,Client進入FIN_WAIT_1狀態。
  2. 第二次揮手:Server收到FIN後,發送一個ACKClient,確認序號爲收到序號seq+1(與SYN相同,一個FIN佔用一個序號),Server進入CLOSE_WAIT狀態。
  3. 第三次揮手:Server發送一個FIN,用來關閉ServerClient的數據傳送,Server進入LAST_ACK狀態。
  4. 第四次揮手:Client收到FIN後,Client進入TIME_WAIT狀態,接着發送一個ACKServer,確認序號爲收到序號seq+1,同時等待2MSL(報文最大生存時間)進入CLOSED狀態。Server收到ACK後進入CLOSED狀態,完成四次揮手。

四次揮手的原因

TCP協議是一種面向連接的、可靠的、基於字節流的傳輸層全雙工模式通信協議,這就意味着,當Client發出FIN報文段時,只是表示Client已經沒有數據要發送了,Client告訴Server,它的數據已經全部發送完畢了;但是,這個時候Client還是可以接受來自Server的數據;當Server返回ACK報文段時,表示它已經知道Client沒有數據發送了,但是Server還是可以發送數據到Client的;當Server也發送了FIN報文段時,這個時候才表示Server也沒有數據要發送了,就會告訴Client,我也沒有數據要發送了,之後彼此就會愉快的中斷這次TCP連接。

Client進入TIME_WAIT狀態後等待2MSL(報文最大生存時間)才進入CLOSED狀態的原因

  1. 保證TCP協議的全雙工連接能夠可靠關閉

如果Client直接CLOSED了,那麼由於IP協議的不可靠性或者是其它網絡原因,導致Server沒有收到Client最後回覆的ACK。那麼Server就會在超時之後繼續發送FIN,此時由於Client已經CLOSED了,就找不到與重發的FIN對應的連接,最後Server就會收到RST而不是ACKServer就會以爲是連接錯誤把問題報告給高層。這樣的情況雖然不會造成數據丟失,但是卻導致TCP協議不符合可靠連接的要求。所以,Client不是直接進入CLOSED,而是要保持TIME_WAIT,當再次收到FIN的時候,能夠保證對方收到ACK,最後正確的關閉連接。

  1. 保證這次連接的重複數據段從網絡中消失

如果Client直接CLOSED,然後又再向Server發起一個新連接,我們不能保證這個新連接與剛關閉的連接的端口號是不同的。也就是說有可能新連接和老連接的端口號是相同的。一般來說不會發生什麼問題,但是還是有特殊情況出現:假設新連接和已經關閉的老連接端口號是一樣的,如果前一次連接的某些數據仍然滯留在網絡中,這些延遲數據在建立新連接之後纔到達Server,由於新連接和老連接的端口號是一樣的,又因爲TCP協議判斷不同連接的依據是socket pair,於是,TCP協議就認爲那個延遲的數據是屬於新連接的,這樣就和真正的新連接的數據包發生混淆了。所以TCP連接還要在TIME_WAIT狀態等待2MSL,這樣可以保證本次連接的所有數據都從網絡中消失。

參考

  1. TCP協議詳解
  2. TCP報文格式詳解
  3. 簡述TCP的三次握手過程
  4. 一次完整的HTTP請求過程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章