有時越簡單的問題,一般就是隐藏着比較大的坑,一般都是需要将問題擴充的。上面求職者的回答不對嗎?當然對,但距離面試官的期望可能還有點距離。
希望大家能帶着如下問題進行閱讀,收獲會更大。
為什麼連接配接的時候是三次握手?
什麼是半連接配接隊列?
ISN(Initial Sequence Number)是固定的嗎?
三次握手過程中可以攜帶資料嗎?
如果第三次握手丢失了,用戶端服務端會如何處理?
SYN攻擊是什麼?
揮手為什麼需要四次?
四次揮手釋放連接配接時,等待2MSL的意義?
三次握手
三次握手 (Three-way Handshake)其實就是指建立一個TCP連接配接時,需要用戶端和伺服器總共發送3個包。進行三次握手的主要作用就是為了确認雙方的接收能力和發送能力是否正常、指定自己的初始化序列号為後面的可靠性傳送做準備。實質上其實就是連接配接伺服器指定端口,建立TCP連接配接,并同步連接配接雙方的序列号和确認号,交換TCP視窗大小資訊。
剛開始用戶端處于 Closed 的狀态,服務端處于 Listen 狀态。
進行三次握手:
第一次握手:用戶端給服務端發一個 SYN 封包,并指明用戶端的初始化序列号 ISN©。此時用戶端處于 SYN_SEND 狀态。
首部的同步位SYN=1,初始序号seq=x,SYN=1的封包段不能攜帶資料,但要消耗掉一個序号。
第二次握手:伺服器收到用戶端的 SYN 封包之後,會以自己的 SYN 封包作為應答,并且也是指定了自己的初始化序列号 ISN(s)。同時會把用戶端的 ISN + 1 作為ACK 的值,表示自己已經收到了用戶端的 SYN,此時伺服器處于 SYN_REVD 的狀态。
在确認封包段中SYN=1,ACK=1,确認号ack=x+1,初始序号seq=y。
第三次握手:用戶端收到 SYN 封包之後,會發送一個 ACK 封包,當然,也是一樣把伺服器的 ISN + 1 作為 ACK 的值,表示已經收到了服務端的 SYN 封包,此時用戶端處于 ESTABLISHED 狀态。伺服器收到 ACK 封包之後,也處于 ESTABLISHED 狀态,此時,雙方已建立起了連接配接。
确認封包段ACK=1,确認号ack=y+1,序号seq=x+1(初始為seq=x,第二個封包段是以要+1),ACK封包段可以攜帶資料,不攜帶資料則不消耗序号。
發送第一個SYN的一端将執行主動打開(active open),接收這個SYN并發回下一個SYN的另一端執行被動打開(passive open)。
在socket程式設計中,用戶端執行connect()時,将觸發三次握手。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iM1AjN2gjNzQTOxgjYyY2YxYzXxQDMxADMxAzLcFDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
為什麼需要三次握手,兩次不行嗎?
弄清這個問題,我們需要先弄明白三次握手的目的是什麼,能不能隻用兩次握手來達到同樣的目的。
第一次握手:用戶端發送網絡包,服務端收到了。
這樣服務端就能得出結論:用戶端的發送能力、服務端的接收能力是正常的。
第二次握手:服務端發包,用戶端收到了。
這樣用戶端就能得出結論:服務端的接收、發送能力,用戶端的接收、發送能力是正常的。不過此時伺服器并不能确認用戶端的接收能力是否正常。
第三次握手:用戶端發包,服務端收到了。
這樣服務端就能得出結論:用戶端的接收、發送能力正常,伺服器自己的發送、接收能力也正常。
是以,需要三次握手才能确認雙方的接收與發送能力是否正常。
試想如果是用兩次握手,則會出現下面這種情況:
如用戶端發出連接配接請求,但因連接配接請求封包丢失而未收到确認,于是用戶端再重傳一次連接配接請求。後來收到了确認,建立了連接配接。資料傳輸完畢後,就釋放了連接配接,用戶端共發出了兩個連接配接請求封包段,其中第一個丢失,第二個到達了服務端,但是第一個丢失的封包段隻是在某些網絡結點長時間滞留了,延誤到連接配接釋放以後的某個時間才到達服務端,此時服務端誤認為用戶端又發出一次新的連接配接請求,于是就向用戶端發出确認封包段,同意建立連接配接,不采用三次握手,隻要服務端發出确認,就建立新的連接配接了,此時用戶端忽略服務端發來的确認,也不發送資料,則服務端一緻等待用戶端發送資料,浪費資源。
什麼是半連接配接隊列?
伺服器第一次收到用戶端的 SYN 之後,就會處于 SYN_RCVD 狀态,此時雙方還沒有完全建立其連接配接,伺服器會把此種狀态下請求連接配接放在一個隊列裡,我們把這種隊列稱之為半連接配接隊列。
當然還有一個全連接配接隊列,就是已經完成三次握手,建立起連接配接的就會放在全連接配接隊列中。如果隊列滿了就有可能會出現丢包現象。
這裡在補充一點關于SYN-ACK 重傳次數的問題:
伺服器發送完SYN-ACK包,如果未收到客戶确認包,伺服器進行首次重傳,等待一段時間仍未收到客戶确認包,進行第二次重傳。如果重傳次數超過系統規定的最大重傳次數,系統将該連接配接資訊從半連接配接隊列中删除。
注意,每次重傳等待的時間不一定相同,一般會是指數增長,例如間隔時間為 1s,2s,4s,8s…
ISN(Initial Sequence Number)是固定的嗎?
當一端為建立連接配接而發送它的SYN時,它為連接配接選擇一個初始序号。ISN随時間而變化,是以每個連接配接都将具有不同的ISN。ISN可以看作是一個32比特的計數器,每4ms加1 。這樣選擇序号的目的在于防止在網絡中被延遲的分組在以後又被傳送,而導緻某個連接配接的一方對它做錯誤的解釋。
三次握手的其中一個重要功能是用戶端和服務端交換 ISN(Initial Sequence Number),以便讓對方知道接下來接收資料的時候如何按序列号組裝資料。如果 ISN 是固定的,攻擊者很容易猜出後續的确認号,是以 ISN 是動态生成的。
三次握手過程中可以攜帶資料嗎?
其實第三次握手的時候,是可以攜帶資料的。但是,第一次、第二次握手不可以攜帶資料
為什麼這樣呢?大家可以想一個問題,假如第一次握手可以攜帶資料的話,如果有人要惡意攻擊伺服器,那他每次都在第一次握手中的 SYN 封包中放入大量的資料。因為攻擊者根本就不理伺服器的接收、發送能力是否正常,然後瘋狂着重複發 SYN 封包的話,這會讓伺服器花費很多時間、記憶體空間來接收這些封包。
也就是說,第一次握手不可以放資料,其中一個簡單的原因就是會讓伺服器更加容易受到攻擊了。而對于第三次的話,此時用戶端已經處于 ESTABLISHED 狀态。對于用戶端來說,他已經建立起連接配接了,并且也已經知道伺服器的接收、發送能力是正常的了,是以能攜帶資料也沒啥毛病。
SYN攻擊是什麼?
伺服器端的資源配置設定是在二次握手時配置設定的,而用戶端的資源是在完成三次握手時配置設定的,是以伺服器容易受到SYN洪泛攻擊。SYN攻擊就是Client在短時間内僞造大量不存在的IP位址,并向Server不斷地發送SYN包,Server則回複确認包,并等待Client确認,由于源位址不存在,是以Server需要不斷重發直至逾時,這些僞造的SYN包将長時間占用未連接配接隊列,導緻正常的SYN請求因為隊列滿而被丢棄,進而引起網絡擁塞甚至系統癱瘓。SYN 攻擊是一種典型的 DoS/DDoS 攻擊。
檢測 SYN 攻擊非常的友善,當你在伺服器上看到大量的半連接配接狀态時,特别是源IP位址是随機的,基本上可以斷定這是一次SYN攻擊。在 Linux/Unix 上可以使用系統自帶的 netstats 指令來檢測 SYN 攻擊。
netstat -n -p TCP | grep SYN_RECV
常見的防禦 SYN 攻擊的方法有如下幾種:
- 縮短逾時(SYN Timeout)時間
- 增加最大半連接配接數
- 過濾網關防護
- SYN cookies技術
四次揮手
建立一個連接配接需要三次握手,而終止一個連接配接要經過四次揮手(也有将四次揮手叫做四次握手的)。這由TCP的半關閉(half-close)造成的。所謂的半關閉,其實就是TCP提供了連接配接的一端在結束它的發送後還能接收來自另一端資料的能力。
TCP 的連接配接的拆除需要發送四個包,是以稱為四次揮手(Four-way handshake),用戶端或伺服器均可主動發起揮手動作。
剛開始雙方都處于 ESTABLISHED 狀态,假如是用戶端先發起關閉請求。四次揮手的過程如下:
第一次揮手:用戶端發送一個 FIN 封包,封包中會指定一個序列号。此時用戶端處于 FIN_WAIT1 狀态。
即發出連接配接釋放封包段(FIN=1,序号seq=u),并停止再發送資料,主動關閉TCP連接配接,進入FIN_WAIT1(終止等待1)狀态,等待服務端的确認。
第二次揮手:服務端收到 FIN 之後,會發送 ACK 封包,且把用戶端的序列号值 +1 作為 ACK 封包的序列号值,表明已經收到用戶端的封包了,此時服務端處于 CLOSE_WAIT 狀态。
即服務端收到連接配接釋放封包段後即發出确認封包段(ACK=1,确認号ack=u+1,序号seq=v),服務端進入CLOSE_WAIT(關閉等待)狀态,此時的TCP處于半關閉狀态,用戶端到服務端的連接配接釋放。用戶端收到服務端的确認後,進入FIN_WAIT2(終止等待2)狀态,等待服務端發出的連接配接釋放封包段。
第三次揮手:如果服務端也想斷開連接配接了,和用戶端的第一次揮手一樣,發給 FIN 封包,且指定一個序列号。此時服務端處于 LAST_ACK 的狀态。
即服務端沒有要向用戶端發出的資料,服務端發出連接配接釋放封包段(FIN=1,ACK=1,序号seq=w,确認号ack=u+1),服務端進入LAST_ACK(最後确認)狀态,等待用戶端的确認。
第四次揮手:用戶端收到 FIN 之後,一樣發送一個 ACK 封包作為應答,且把服務端的序列号值 +1 作為自己 ACK 封包的序列号值,此時用戶端處于 TIME_WAIT 狀态。需要過一陣子以確定服務端收到自己的 ACK 封包之後才會進入 CLOSED 狀态,服務端收到 ACK 封包之後,就處于關閉連接配接了,處于 CLOSED 狀态。
即用戶端收到服務端的連接配接釋放封包段後,對此發出确認封包段(ACK=1,seq=u+1,ack=w+1),用戶端進入TIME_WAIT(時間等待)狀态。此時TCP未釋放掉,需要經過時間等待計時器設定的時間2MSL後,用戶端才進入CLOSED狀态。
收到一個FIN隻意味着在這一方向上沒有資料流動。用戶端執行主動關閉并進入TIME_WAIT是正常的,服務端通常執行被動關閉,不會進入TIME_WAIT狀态。
在socket程式設計中,任何一方執行close()操作即可産生揮手操作。
揮手為什麼需要四次?
因為當服務端收到用戶端的SYN連接配接請求封包後,可以直接發送SYN+ACK封包。其中ACK封包是用來應答的,SYN封包是用來同步的。但是關閉連接配接時,當服務端收到FIN封包時,很可能并不會立即關閉SOCKET,是以隻能先回複一個ACK封包,告訴用戶端,“你發的FIN封包我收到了”。隻有等到我服務端所有的封包都發送完了,我才能發送FIN封包,是以不能一起發送。故需要四次揮手。
2MSL等待狀态
TIME_WAIT狀态也成為2MSL等待狀态。每個具體TCP實作必須選擇一個封包段最大生存時間MSL(Maximum Segment Lifetime),它是任何封包段被丢棄前在網絡内的最長時間。這個時間是有限的,因為TCP封包段以IP資料報在網絡内傳輸,而IP資料報則有限制其生存時間的TTL字段。
對一個具體實作所給定的MSL值,處理的原則是:當TCP執行一個主動關閉,并發回最後一個ACK,該連接配接必須在TIME_WAIT狀态停留的時間為2倍的MSL。這樣可讓TCP再次發送最後的ACK以防這個ACK丢失(另一端逾時并重發最後的FIN)。
這種2MSL等待的另一個結果是這個TCP連接配接在2MSL等待期間,定義這個連接配接的插口(客戶的IP位址和端口号,伺服器的IP位址和端口号)不能再被使用。這個連接配接隻能在2MSL結束後才能再被使用。
四次揮手釋放連接配接時,等待2MSL的意義?
MSL是Maximum Segment Lifetime的英文縮寫,可譯為“最長封包段壽命”,它是任何封包在網絡上存在的最長時間,超過這個時間封包将被丢棄。
為了保證用戶端發送的最後一個ACK封包段能夠到達伺服器。因為這個ACK有可能丢失,進而導緻處在LAST-ACK狀态的伺服器收不到對FIN-ACK的确認封包。伺服器會逾時重傳這個FIN-ACK,接着用戶端再重傳一次确認,重新啟動時間等待計時器。最後用戶端和伺服器都能正常的關閉。假設用戶端不等待2MSL,而是在發送完ACK之後直接釋放關閉,一但這個ACK丢失的話,伺服器就無法正常的進入關閉連接配接狀态。
兩個理由:
保證用戶端發送的最後一個ACK封包段能夠到達服務端。
這個ACK封包段有可能丢失,使得處于LAST-ACK狀态的B收不到對已發送的FIN+ACK封包段的确認,服務端逾時重傳FIN+ACK封包段,而用戶端能在2MSL時間内收到這個重傳的FIN+ACK封包段,接着用戶端重傳一次确認,重新啟動2MSL計時器,最後用戶端和服務端都進入到CLOSED狀态,若用戶端在TIME-WAIT狀态不等待一段時間,而是發送完ACK封包段後立即釋放連接配接,則無法收到服務端重傳的FIN+ACK封包段,是以不會再發送一次确認封包段,則服務端無法正常進入到CLOSED狀态。
防止“已失效的連接配接請求封包段”出現在本連接配接中。
用戶端在發送完最後一個ACK封包段後,再經過2MSL,就可以使本連接配接持續的時間内所産生的所有封包段都從網絡中消失,使下一個新的連接配接中不會出現這種舊的連接配接請求封包段。
為什麼TIME_WAIT狀态需要經過2MSL才能傳回到CLOSE狀态?
理論上,四個封包都發送完畢,就可以直接進入CLOSE狀态了,但是可能網絡是不可靠的,有可能最後一個ACK丢失。是以TIME_WAIT狀态就是用來重發可能丢失的ACK封包。
總結
《TCP/IP詳解 卷1:協定》有一張TCP狀态變遷圖,很具有代表性,有助于大家了解三次握手和四次揮手的狀态變化。如下圖所示,粗的實線箭頭表示正常的用戶端狀态變遷,粗的虛線箭頭表示正常的伺服器狀态變遷。
以後有人問你三次握手和四次揮手,直接把這一篇文章丢給他就可以了,他想問的都在這裡。
參考:《TCP/IP詳解 卷1:協定》