面試官問我TCP三次握手和四次揮手,我真的是

Java3y發表於2021-12-24

候選者面試官你好,請問面試可以開始了嗎

面試官:嗯,開始吧

面試官今天來聊聊TCP吧,TCP的各個狀態還有印象嗎?

候選者:還有些許印象的,要不我就來簡單說下TCP的三次握手和四次揮手的流程吧

候選者:說完這兩個流程,就能把TCP的狀態給涵蓋上了

面試官:可以吧

候選者:在說TCP的三次握手和四次揮手之前,我先給你畫下TCP的頭部格式唄(:

候選者:對於TCP三次握手和四次揮手,我們最主要的就是關注TCP頭部的序列號、確認號以及幾個標記位(SYN/FIN/ACK/RST)

候選者:序列號:在初次建立連線的時候,客戶端和服務端都會為「本次的連線」隨機初始化一個序列號。(縱觀整個TCP流程中,序列號可以用來解決網路包亂序的問題)

候選者:確認號:該欄位表示「接收端」告訴「傳送端」對上一個資料包已經成功接收(確認號可以⽤來解決網路包丟失的問題)

候選者:而標記位就很好理解啦。SYN為1時,表示希望建立連線。ACK為1時,確認號欄位有效。FIN為1時,表示希望斷開連線。RST為1時,表示TCP連線出現異常,需要斷開。

候選者:下面就先從三次握手開始吧,期間我也會在三次握手中涉及到的TCP狀態也說下的。

候選者:TCP三次握手的過程其實就是在:確認通訊雙方(客戶端和服務端)的序列號

候選者:它的過程是這樣的

候選者:在最開始的時候,客戶端和服務端都處於 CLOSE 狀態

候選者:伺服器主動監聽某個埠,處於 LISTEN 狀態

候選者:客戶端會隨機生成出序列號(這裡的序列號一般叫做client_isn),並且把標誌位設定為SYN(意味著要連線),然後把該報文傳送給服務端

候選者:客戶端傳送完SYN報文以後,自己便進入了 SYN_SEND 狀態

候選者:服務端接收到了客戶端的請求之後,自己也初始化對應的序列號(這裡的序列號一般叫做 server_isn)

候選者:在「確認號」欄位裡填上client_isn + 1(相當於告訴客戶端,已經收到了傳送過來的序列號了) ,並且把 SYN 和 ACK 標記位都點亮(置為1)

候選者:把該報文傳送客戶端,服務端的狀態變成 SYN-REVD 狀態

候選者:客戶端收到服務端傳送的報文後,就知道服務端已經接收到了自己的序列號(通過確認號就可以知道),並且接收到了服務端的序列號(server_isn)

候選者:此時,客戶端需要告訴服務端自己已經接收到了他傳送過來的序列號,所以在「確認號」欄位上填上server_isn+1,,並且標記位 ACK 為1

候選者:客戶端在傳送報文之後,進入 ESTABLISHED 狀態,而服務端接收到客戶端的報文之後,也進入 ESTABLISHED 狀態

候選者:這就是三次握手的過程以及涉及到的TCP狀態

候選者:總結下來,就是雙方都把自身的序列號發給對方,看對方能不能接收到。如果「確認可以」,那就可以正常通訊。(三次握手這個過程就可以看到雙方都有接收和傳送的能力)

面試官那兩次握手行嗎?

候選者:兩次握手只能保證客戶端的序列號成功被服務端接收,而服務端是無法確認自己的序列號是否被客戶端成功接收。所以是不行的(:

面試官瞭解了,那我想問問序列號為什麼是隨機的?以及序列號是怎麼生成的?

候選者:一方面為了安全性(隨機ISN能避免非同一網路的攻擊),另一方面可以讓通訊雙方能夠根據序號將「不屬於」本連線的報文段丟棄

候選者:序列號怎麼生成的?這…隨便猜下就應該跟「時鐘」和TCP頭部的某些屬性做運算生成的吧,類似於雪花演算法(:具體我忘了。

面試官既然網路是不可靠的,那建立連線不是會經過三次握手嗎?那要是在中途丟了,怎麼辦?

候選者:假設第一個包丟了,客戶端傳送給服務端的 SYN 包丟了(簡而要之就是服務端沒接收到客戶端的SYN包)

候選者:客戶端遲遲收不到服務端的ACK包,那會週期性超時重傳,直到收到服務端的ACK

候選者:假設第二個包丟了,服務端傳送的SYN+ACK包丟了(簡而要之就是客戶端沒接收到服務端的SYN+ACK包)

候選者:服務端遲遲收不到客戶端的ACK包,那會週期性超時重傳,直到收到客戶端的ACK

候選者:假設第三個包丟了(ACK包),客戶端傳送完第三個包後單方面進入了 ESTABLISHED 狀態,而服務端也認為此時連線是正常的,但第三個包沒到達服務端

候選者:一、如果此時客戶端與服務端都還沒資料傳送,那服務端會認為自己傳送的SYN+ACK的包沒傳送至客戶端,所以會超時重傳自己的SYN+ACK包

候選者:二、如果這時候客戶端已經要傳送資料了,服務端接收到了ACK + Data資料包,那自然就切換到 ESTABLISHED 狀態下,並且接收客戶端的Data資料包

候選者:三、如果此時服務端要傳送資料了,但傳送不了,會一直週期性超時重傳SYN + ACK,直到接收到客戶端的ACK包

面試官嗯,是不是要講下四次揮手了?

候選者:嗯,在建立完連線之後,客戶端和服務端雙方都處於 ESTABLISHED 狀態狀態

候選者:斷開連線雙方都有權利的,下面我還是以客戶端主動斷開為例好啦。

候選者:客戶端打算關閉連線,會發 FIN 報文給服務端(其實就是把標誌位 FIN 點亮),客戶端傳送完之後,就進入FIN_WAIT_1狀態

候選者:服務端收到 FIN 報文之後,回覆 ACK 報文給客戶端(表示已經收到了),服務端傳送完之後,就進入 CLOSE_WAIT 狀態

候選者:客戶端接收到服務端的 ACK 報文,就進入了 FIN_WAIT_2 狀態

候選者:這時候,伺服器可能還有資料要傳送給客戶端,等服務端確認自己已經沒有資料返回給客戶端之後,就傳送FIN報文給客戶端了,自己進入 LAST_ACK 狀態

候選者:客戶端收到服務端的FIN報文之後,回應ACK報文,自己進入 TIME_WAIT 狀態

候選者:服務端收到客戶端的ACK報文之後,服務端就進入 CLOSE 狀態

候選者:客戶端在TIME_WAIT等到2MSL,也進入了 CLOSE 狀態

候選者:四次揮手的流程到這裡就結束了,結合三次握手,TCP的各個狀態也已經說完了。

面試官:嗯嗯,剛聊完四次揮手嘛,那你覺得為什麼是四次呢?

候選者:其實很好理解,當客戶端第一次傳送 FIN 報文之後,只是代表著客戶端不再傳送資料給服務端,但此時客戶端還是有接收資料的能力的。而服務端收到FIN報文的時候,可能還有資料要傳輸給客戶端,所以只能先回復 ACK給客戶端

候選者:等到服務端不再有資料傳送給客戶端時,才傳送 FIN 報文給客戶端,表示可以關閉了。

候選者:所以,一來一回就四次了。

面試官從四次揮手的流程上來看,有個 TIME_WAIT 狀態,你知道這個狀態幹什麼用的嗎?(等待 2MSL)

候選者:主要有兩個原因吧。1.保證最後的 ACK 報文 「接收方」一定能收到(如果收不到,對方會 重發 FIN 報文)2. 確保在建立新連線時,先前網路中殘餘的資料都丟失了

候選者:其實也比較好理解的。就正如我們重啟伺服器一樣,會先優雅關閉各種資源,再留有一段時間,希望在這段時間內,資源是正常關閉的,這樣重啟伺服器(或者釋出)就基本認為不會影響到線上執行了。

面試官假設 TIME_WAIT 狀態多過會有什麼危害?怎麼解決呢?

候選者:從流程上看, TIME_WAIT 狀態 只會出現在 主動發起 關閉連線的一方。危害就是會佔用記憶體資源和埠唄(畢竟在等待嘛),解決的話,有Linux引數可以設定,具體忘了額。

面試官:今天最後再問個問題吧,我們常說TCP連線,那這個連線到底是什麼?你是怎麼理解的?

候選者:其實從三次握手可以發現的是,TCP建立連線無非就是交換了雙方的狀態(比如序列號)。然後就沒有然後了…連線本質上「只是互相維持一個狀態,有連線特性」

面試官:好吧。

關注我的微信公眾號【Java3y】來聊點不一樣的!

【對線面試官+從零編寫Java專案】 持續高強度更新中!求star!!

原創不易!!求三連!!

相關文章