不要再讓面試官問三次握手和四次揮手了

記住一句話:面試時(shí)越簡單的問題,一般就是隱藏著比較大的坑,一般都是需要將問題擴(kuò)展的。

三次握手

三次握手 (Three-way Handshake)其實(shí)就是指建立一個(gè)TCP連接時(shí),需要客戶端和服務(wù)端總共發(fā)送 3 個(gè)包。進(jìn)行三次握手的主要作用是為了確認(rèn)雙方的接受能力和發(fā)送能力是否正常,并指定自己的初始化序列號(hào)、為后面的可靠性傳送做準(zhǔn)備。
實(shí)質(zhì)上就是連接服務(wù)器指定端口,建立TCP連接,并同步連接雙方的序列號(hào)和確認(rèn)號(hào),交換TCP窗口大小信息。

剛開始客戶端處于 Closed 的狀態(tài),服務(wù)端處于 Listen 狀態(tài);

進(jìn)行三次握手

  • 第一次握手:客戶端給服務(wù)端發(fā)一個(gè) SYN 報(bào)文,并指明客戶端的初始化序列號(hào) ISN(c)。此時(shí)客戶端處于 SYN_SEND 狀態(tài);
    首部的同步位SYN=1,初始序號(hào)seq=x,SYN=1的報(bào)文段不能攜帶數(shù)據(jù),但要消耗掉一個(gè)序號(hào)。
  • 第二次握手:服務(wù)端收到客戶端的 SYN 報(bào)文之后,會(huì)以自己的SYN報(bào)文作為應(yīng)答,并且也指定了自己的初始化序列號(hào) ISN(s)。同時(shí)會(huì)把客戶端的ISN+1作為ACK的值,表示自己已經(jīng)收到了客戶端的SYN。此時(shí)服務(wù)器處于SYN_REVD的狀態(tài)。
    在確認(rèn)報(bào)文段中SYN=1,ACK=1,確認(rèn)號(hào)ack=x+1,初始序號(hào)seq=y。
  • 第三次握手:客戶端收到SYN報(bào)文之后,會(huì)發(fā)送一個(gè)ACK報(bào)文,當(dāng)然也是一樣把服務(wù)器的ISN+1作為ACK的值,表示已經(jīng)收到了服務(wù)器的SYN報(bào)文。此時(shí)客戶端處于ESTABLISHED狀態(tài)。
    服務(wù)器收到ACK報(bào)文之后,也處于ESTABLISHED狀態(tài)。此時(shí)雙方已建立起了連接。

確認(rèn)報(bào)文段ACK=1,確認(rèn)號(hào)ack=y+1,序號(hào)seq=x+1(初始為seq=x,所以第二個(gè)報(bào)文段要+1),ACK報(bào)文段可以攜帶數(shù)據(jù),不攜帶數(shù)據(jù)則不消耗序號(hào)。

發(fā)送第一個(gè)SYN的一端將執(zhí)行主動(dòng)打開,接收這個(gè)SYN并發(fā)回下一個(gè)SYN的另一端執(zhí)行被動(dòng)打開。

socket編程中,客戶端執(zhí)行connect()時(shí),將觸發(fā)三次握手。

三次握手.png
  1. 為什么需要三次握手,兩次不行嗎?
    弄清楚這個(gè)問題,首先需要搞明白三次握手的目的是什么,能不能只用兩次握手來達(dá)到同樣的目的。

    • 第一次握手:客戶端發(fā)送網(wǎng)絡(luò)包,服務(wù)端收到了。
      這樣服務(wù)器就能得出結(jié)論:客戶端的發(fā)送能力、服務(wù)器端的接收能力是正常的。
    • 第二次握手:服務(wù)端發(fā)包,客戶端收到了。
      這樣客戶的就能得出結(jié)論:服務(wù)端的接收、發(fā)送能力,客戶端的接收、發(fā)送能力是正常的。
      不過,此時(shí)的服務(wù)器并不能確認(rèn)客戶端的接收能力是否正常
    • 第三次握手:客戶端發(fā)包,服務(wù)器收到了。
      這樣服務(wù)端就能得到結(jié)論:客戶端的接收、發(fā)送能力正常,自己的發(fā)送、接收能力也正常。

    故:需要三次握手才能確認(rèn)雙方的接收與發(fā)送能力是否正常!

    試想一下,如果是用兩次握手,則會(huì)出現(xiàn)以下情況:

客戶端發(fā)送連接請求,但因連接請求報(bào)文丟失而未收到確認(rèn),于是客戶端再次發(fā)送一次連接請求。后來收到了確認(rèn),建立了連接。數(shù)據(jù)傳輸完畢后,就釋放了連接,客戶端共發(fā)出了兩個(gè)連接請求報(bào)文,其中第一個(gè)丟失,第二個(gè)到達(dá)了服務(wù)端,但第一個(gè)丟失的報(bào)文段只是在某些網(wǎng)絡(luò)結(jié)點(diǎn)長時(shí)間滯留了,延誤到連接釋放以后的某個(gè)時(shí)間才到達(dá)服務(wù)端,此時(shí)服務(wù)端誤認(rèn)為客戶端又發(fā)來了一次新的連接請求,于是就向客戶端發(fā)出確認(rèn)報(bào)文段,同意建立連接,不采用三次握手,只要服務(wù)端發(fā)出確認(rèn),新的連接就已經(jīng)建立了。而此時(shí)客戶端忽略了服務(wù)端發(fā)來的確認(rèn)包,也不發(fā)送數(shù)據(jù),則服務(wù)端一直等待客戶端發(fā)送數(shù)據(jù),浪費(fèi)服務(wù)器資源。

  1. 什么是半連接隊(duì)列?
    服務(wù)器第一次收到客戶端的SYN之后,就會(huì)處于SYN_RCVD狀態(tài),此時(shí)雙方還沒有完全建立起連接,服務(wù)器會(huì)把此種狀態(tài)下的請求連接放入一個(gè)隊(duì)列中,此隊(duì)列稱之為半連接隊(duì)列。

    當(dāng)然還有一個(gè)全連接隊(duì)列,就是已經(jīng)完成了三次握手,建立起的連接被放入的隊(duì)列。如果隊(duì)列滿了,就可能會(huì)出現(xiàn)丟包現(xiàn)象。

    關(guān)于SYN_ACK重傳次數(shù)的問題:

服務(wù)器發(fā)送完SYN_ACK后,如果未收到客戶端確認(rèn)包,服務(wù)端進(jìn)行首次重傳;等待一段時(shí)間仍未收到確認(rèn)包,則進(jìn)行第二次重傳;如果重傳次數(shù)超過系統(tǒng)規(guī)定的最大重傳次數(shù),系統(tǒng)將該連接信息從半連接隊(duì)列中刪除。
注意:每次重傳所等待的時(shí)間不一定相同,一般會(huì)是指數(shù)增長,如1s、2s、4s、8s ...

  1. ISN是固定的嗎?
    當(dāng)一端為建立連接而發(fā)送它的SYN時(shí),它為連接選擇一個(gè)初始序號(hào)。ISN隨時(shí)間而變化,因此每個(gè)連接都將具有不同的ISN。

    ISN可以看作是一個(gè)32bit的計(jì)數(shù)器,每4ms1。這樣選擇序號(hào)的目的在于防止在網(wǎng)絡(luò)中被延遲的分組在以后又被傳送,而導(dǎo)致某個(gè)連接的一方對它做錯(cuò)誤的解釋。

    三次握手中的一個(gè)重要功能是客戶端和服務(wù)端交換ISN(Initial Sequence Number),以便讓對方知道接下來接收數(shù)據(jù)時(shí)如何按序列號(hào)組裝數(shù)據(jù)。如果ISN是固定的,攻擊者很容易猜出后續(xù)的確認(rèn)號(hào),因此ISN是動(dòng)態(tài)生成的。

  2. 三次握手過程中可以攜帶數(shù)據(jù)嗎?
    第三次握手可以攜帶數(shù)據(jù)。但前兩次是不可以的。

試想一下,假如第一次可以攜帶數(shù)據(jù),如果有人要惡意攻擊服務(wù)器,那他每次都在第一次握手中的SYN報(bào)文中放入大量的數(shù)據(jù)。因?yàn)楣粽吒静魂P(guān)心服務(wù)器的接收、發(fā)送能力是否正常,然后瘋狂重發(fā)SYN報(bào)文,這會(huì)讓服務(wù)器花費(fèi)大量時(shí)間和內(nèi)容空間來接收報(bào)文。
也就是說,第一次握手不可以放數(shù)據(jù),其中一個(gè)最簡單的原因就是會(huì)讓服務(wù)器更容易收到攻擊。
而第三次握手時(shí),客戶端已經(jīng)處于ESTABLISHED狀態(tài),對客戶端來說,連接已經(jīng)建立了,也確認(rèn)服務(wù)端的接收、發(fā)送能力正常,所以能攜帶數(shù)據(jù)。

  1. SYN攻擊是什么?
    服務(wù)器端的資源分配是在二次握手時(shí)分配的,而客戶端的資源是在完成三次握手時(shí)分配的,所以服務(wù)器容易收到SYN泛洪攻擊。

SYN攻擊就是客戶端在短時(shí)間內(nèi)偽造大量不存在的IP地址,并向服務(wù)器不斷地發(fā)送SYN包,服務(wù)端則回復(fù)確認(rèn)包,并等待客戶端確認(rèn)。由于源地址不存在,因此服務(wù)端需要不斷重發(fā)直至超時(shí),這些偽造的SYN包將長時(shí)間占用未連接隊(duì)列,導(dǎo)致正常的SYN請求因?yàn)殛?duì)列滿而被丟棄,從而引起網(wǎng)絡(luò)擁塞甚至系統(tǒng)癱瘓。
SYN攻擊是一種典型 DoS/DDoS 的攻擊。

檢測SYN攻擊非常方便,當(dāng)你在服務(wù)器上看到大量的半連接狀態(tài)時(shí),特別是源IP地址是隨機(jī)的,基本上可以斷定這是一次SYN攻擊。在Linux/Unix上可以使用系統(tǒng)自帶的 netstats 命令來檢測SYN攻擊:
netstats -n -p TCP | grep SYN_RECV

常見的防御措施:

  • 縮短超時(shí)時(shí)間(SYN Timeout)
  • 增加最大半連接數(shù)
  • 過濾網(wǎng)關(guān)防護(hù)
  • SYN cookies技術(shù)

四次揮手

建立一個(gè)連接需要三次握手,而終止一個(gè)連接要經(jīng)過四次揮手(也有人叫四次握手)。這是由TCP的半關(guān)閉(half-close)造成的。
所謂半關(guān)閉,其實(shí)就是TCP提供了連接的一端在它結(jié)束自己的發(fā)送后,還能接受來自另一端數(shù)據(jù)的能力。

TCP連接的拆除需要發(fā)送四個(gè)包,因此稱為四次揮手 (Four-way handshake),客戶端或服務(wù)器均可主動(dòng)發(fā)起揮手動(dòng)作。

剛開始雙方都處于ESTABLISHED狀態(tài),加入是客戶端先發(fā)起關(guān)閉請求,四次揮手的過程如下:

  • 第一次揮手:客戶端發(fā)送一個(gè)FIN報(bào)文,報(bào)文中會(huì)指定一個(gè)序列號(hào)。此時(shí)客戶端處于FIN_WAIT1狀態(tài);

    即發(fā)出連接釋放報(bào)文段(FIN=1,序號(hào)seq=u),并停止再發(fā)送數(shù)據(jù),主動(dòng)關(guān)閉TCP連接,進(jìn)入FIN_WAIT1(終止等待1)狀態(tài),等待服務(wù)端的確認(rèn)。

  • 第二次揮手:服務(wù)端收到FIN包之后,會(huì)發(fā)送ACK報(bào)文,且把客戶端的序列號(hào)值+1 作為ACK報(bào)文序列號(hào)值,表明已經(jīng)收到客戶端的報(bào)文了,此時(shí)服務(wù)端處于CLOSE_WAIT狀態(tài);

    即服務(wù)端收到連接釋放的報(bào)文段后,立即發(fā)出確認(rèn)報(bào)文段(ACK=1,確認(rèn)號(hào)ack=u+1,序號(hào)seq=v),服務(wù)端進(jìn)入CLOSE_WAIT(關(guān)閉等待)狀態(tài),此時(shí)的TCP處于半關(guān)閉狀態(tài),客戶端到服務(wù)端的連接釋放??蛻舳耸盏椒?wù)端的確認(rèn)后,進(jìn)入FIN_WAIT2(終止等待2)狀態(tài),等待服務(wù)端發(fā)出的連接釋放報(bào)文段;

  • 第三次揮手:如果服務(wù)端也想斷開連接,和客戶端的第一次揮手一樣,發(fā)送FIN報(bào)文,且指定一個(gè)序列號(hào)。此時(shí)服務(wù)端處于LAST_ACK狀態(tài)。

    即服務(wù)端沒有要向客戶端發(fā)送的數(shù)據(jù),服務(wù)端發(fā)出連接釋放的報(bào)文段(FIN=1,ACK=1,序號(hào)seq=w,確認(rèn)號(hào)ack=u+1),服務(wù)端進(jìn)入LAST_ACK(最后確認(rèn))狀態(tài),等待客戶端確認(rèn)。

  • 第四次揮手:客戶端收到FIN之后,一樣發(fā)送一個(gè)ACK報(bào)文作為應(yīng)答,且把服務(wù)端的序列號(hào)值+1 作為自己的ACK報(bào)文的序列號(hào)值,此時(shí)客戶端處于TIME_WAIT狀態(tài),需要過一陣子才會(huì)進(jìn)入CLOSED狀態(tài),為了確保服務(wù)端收到自己的ACK報(bào)文。服務(wù)端收到ACK報(bào)文之后,就會(huì)關(guān)閉連接,也進(jìn)入CLOSED狀態(tài)。

    即客戶端收到服務(wù)端的連接釋放報(bào)文段后,對此發(fā)出確認(rèn)報(bào)文段(ACK=1,seq=u+1,ack=w+1),客戶端進(jìn)入TIME_WAIT(時(shí)間等待)狀態(tài)。此時(shí)TCP未釋放掉,需要經(jīng)過時(shí)間等待計(jì)數(shù)器設(shè)置的時(shí)間2MSL后,客戶端才會(huì)進(jìn)入CLOSED狀態(tài)。

收到一個(gè)FIN只意味著這一方向上沒有數(shù)據(jù)流動(dòng),客戶端執(zhí)行主動(dòng)關(guān)閉并進(jìn)入TIME_WAIT狀態(tài)是正常,服務(wù)端通常執(zhí)行被動(dòng)關(guān)閉,不會(huì)進(jìn)入TIME_WAIT狀態(tài)。

socket編程中,任何一方執(zhí)行 close() 操作即可產(chǎn)生回收操作。

四次揮手.png
  1. 揮手為什么需要四次?
    當(dāng)服務(wù)端收到客戶端的SYN連接請求報(bào)文后,可以直接發(fā)送SYN+ACK報(bào)文,其中ACK報(bào)文是用來應(yīng)答的,SYN報(bào)文是用來同步的。
    但關(guān)閉連接時(shí),當(dāng)服務(wù)端收到FIN報(bào)文時(shí),很可能并不會(huì)立即關(guān)閉SOCKET,所以只能先回復(fù)一個(gè)ACK報(bào)文,告訴客戶端:“我收到了你發(fā)的FIN報(bào)文”。只有等待服務(wù)端所有的報(bào)文都發(fā)送完了,才能發(fā)送FIN報(bào)文,因此不能一起發(fā)送ACK+FIN。
    故需要四次揮手。

  2. 2MSL等待狀態(tài)
    TIME_WAIT狀態(tài)也稱為2MSL等待狀態(tài)。每個(gè)具體TCP實(shí)現(xiàn)必須選擇一個(gè)報(bào)文段的最大生存時(shí)間--MSL(Maximum Segment Lifetime),它是任何報(bào)文段被被丟棄前在網(wǎng)絡(luò)中的最長時(shí)間。這個(gè)時(shí)間是有限的,因?yàn)門CP報(bào)文段以IP數(shù)據(jù)包在網(wǎng)絡(luò)中傳輸,而IP數(shù)據(jù)包有限制其生存時(shí)間的TTL字段。

    對一個(gè)具體實(shí)現(xiàn)所給定的MSL值,處理原則是:當(dāng)TCP執(zhí)行一個(gè)主動(dòng)關(guān)閉,并發(fā)回最后一個(gè)ACK時(shí),該連接必須在TIME_WAIT狀態(tài)停留的時(shí)間為2倍的MSL。這樣可讓TCP再次發(fā)送最后的ACK,以防這個(gè)ACK丟失(另一端超時(shí)并重發(fā)最后的FIN)。

    這種2MSL等待的另一個(gè)結(jié)果是,這個(gè)TCP連接在2MSL等待期間,定義這個(gè)連接的插口(客戶端的IP地址和端口號(hào),服務(wù)器的IP地址和端口號(hào))不能再被使用。這個(gè)連接只能在2MSL結(jié)束后才能再被使用。

  3. 四次揮手釋放連接時(shí),等待2MSL的意義?
    2MSL:Maximum Segment Lifetime,最長報(bào)文段壽命,它是任何報(bào)文在網(wǎng)絡(luò)上存在的最長時(shí)間,超過這個(gè)時(shí)間的報(bào)文將被丟棄。

    為了保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文段能到達(dá)服務(wù)器。因?yàn)檫@個(gè)ACK有可能丟失,從而導(dǎo)致處在LAST-ACK狀態(tài)的服務(wù)器收不到FIN-ACK的確認(rèn)報(bào)文。服務(wù)器會(huì)超時(shí)重傳這個(gè)FIN-ACK報(bào)文,接著客戶端再重傳一次確認(rèn),重新啟動(dòng)時(shí)間等待計(jì)時(shí)器。最后客戶端和服務(wù)端都能正常的關(guān)閉。
    假設(shè)客戶端不等待2MSL,而是在發(fā)送完ACK之后直接釋放關(guān)閉,一旦這個(gè)ACK丟失,那么服務(wù)器就無法正常的進(jìn)入關(guān)閉連接狀態(tài)。

    兩個(gè)理由:

    • 保證客戶端發(fā)送的最后一個(gè)ACK報(bào)文段能夠到達(dá)服務(wù)端。
      這個(gè)ACK報(bào)文段有可能丟失,使處于LAST_ACK狀態(tài)的服務(wù)器收不到對已發(fā)送的FIN-ACK報(bào)文段的確認(rèn)。那么,服務(wù)端將會(huì)超時(shí)重傳FIN-ACK報(bào)文段。而客戶端能在2MSL時(shí)間內(nèi)收到這個(gè)重傳的FIN-ACK報(bào)文段,接著客戶端也會(huì)重傳一次確認(rèn)ACK報(bào)文,重新啟動(dòng)2MSL計(jì)時(shí)器,最后客戶端和服務(wù)端都進(jìn)入CLOSED狀態(tài)。
      如果客戶端在TIME_WAIT狀態(tài)不等待一段時(shí)間,而是在發(fā)完ACK報(bào)文段后立即釋放連接,則無法收到服務(wù)端重傳的FIN-ACK報(bào)文段,所以不會(huì)再發(fā)送一次ACK報(bào)文端,導(dǎo)致服務(wù)端無法正常進(jìn)入CLOSED狀態(tài)。
    • 防止“已失效的連接請求報(bào)文段”出現(xiàn)在本地連接中。
      客戶端在發(fā)送完最后一個(gè)ACK報(bào)文段后,再經(jīng)過2MSL,就可以使本次連接持續(xù)的時(shí)間內(nèi)所產(chǎn)生的所有報(bào)文段都從網(wǎng)絡(luò)中消失,使下一個(gè)新的連接中不會(huì)出現(xiàn)這種舊的連接請求報(bào)文段。
  4. 為什么TIME_WAIT狀態(tài)需要經(jīng)過2MSL才能返回到CLOSED狀態(tài)?
    理論上,四個(gè)報(bào)文都發(fā)送完畢就可以直接進(jìn)入CLOSED狀態(tài)了,但可能網(wǎng)絡(luò)是不可靠的,有可能最后一個(gè)ACK丟失,所以TIME_WAIT狀態(tài)就是用來重發(fā)可能丟失的ACK報(bào)文。

總結(jié)

《TCP/IP詳解 卷1:協(xié)議》有一張TCP狀態(tài)變遷圖,很具有代表性,有助于理解三次握手和四次揮手的狀態(tài)變化。
如下圖所示,粗的實(shí)線箭頭表示正常的客戶端狀態(tài)變遷,粗的虛線箭頭表示正常的服務(wù)器狀態(tài)變遷。

TCP狀態(tài)變遷圖.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容