三次握手建立連線
TCP(傳輸控制協議)的三次握手機制是一種用於在兩個 TCP 主機之間建立一個可靠的連線的過程。這個機制確保了兩端的通訊是同步的,並且在資料傳輸開始前,雙方都準備好了進行通訊。
①、第一次握手:SYN(最開始都是 CLOSE,之後伺服器進入 LISTEN)
- 發起連線:客戶端傳送一個 TCP 報文段到伺服器。這個報文段的頭部中,SYN 位被設定為 1,表明這是一個連線請求。同時,客戶端會隨機選擇一個序列號(Sequence Number),假設為 x,傳送給伺服器。
- 目的:客戶端通知伺服器它希望建立連線,並告知伺服器自己的初始序列號。
- 狀態:客戶端進入 SYN_SENT 狀態。
②、第二次握手:SYN+ACK
- 確認並應答:伺服器收到客戶端的連線請求後,如果同意建立連線,它會傳送一個應答 TCP 報文段給客戶端。在這個報文段中,SYN 位和 ACK 位都被設定為 1。伺服器也會選擇自己的一個隨機序列號,假設為 y,並將客戶端的序列號加 1(即 x+1)作為確認號(Acknowledgment Number),傳送給客戶端。
- 目的:伺服器告訴客戶端,它的連線請求被接受了,並通知客戶端自己的初始序列號。
- 狀態:伺服器進入 SYN_RCVD 狀態。
③、第三次握手:ACK
- 最終確認:客戶端收到伺服器的應答後,還需要向伺服器傳送一個確認。這個 TCP 報文段的 ACK 位被設定為 1,確認號被設定為伺服器序列號加 1(即 y+1),而自己的序列號是 x+1。
- 目的:客戶端確認收到了伺服器的同步應答,完成三次握手,建立連線。
- 狀態:客戶端進入 ESTABLISHED 狀態,當伺服器接收到這個包時,也進入 ESTABLISHED 狀態
為什麼 TCP 握手不能是兩次?
為了防止伺服器一直等。(ACK=1 的確認報文段因為某些原因在傳輸過程中丟失了,客戶端選擇重新建立連線,而服務端還一直等待這一次連線)
為了防止客戶端已經失效的連線請求突然又傳送到了伺服器。(一箇舊的、延遲的連線請求(SYN=1)被伺服器接受,導致伺服器錯誤地開啟一個不再需要的連線。)
第三次握手服務端未收到客戶端傳送過來的 ACK 報文
服務端同樣會採用類似客戶端的超時重傳機制,如果重試次數超過限制,則 accept()呼叫返回-1,服務端建立連線失敗;而此時客戶端認為自己已經建立連線成功,因此開始向服務端傳送資料,但是服務端的 accept()系統呼叫已經返回,此時不在監聽狀態,因此服務端接收到客戶端傳送來的資料時會傳送 RST 報文給客戶端,消除客戶端單方面建立連線的狀態
SYN Flood
SYN Flood 是一種典型的 DDos 攻擊,它在短時間內,偽造不存在的 IP 地址, 向伺服器傳送大量 SYN 報文。當伺服器回覆 SYN+ACK 報文後,不會收到 ACK 回應報文,那麼 SYN 佇列裡的連線舊不會出對隊,久⽽久之就會佔滿服務端的 SYN 接收佇列(半連線佇列),使得伺服器不能為正常⽤戶服務。
SYN Flood應對策略
syn cookie:在收到 SYN 包後,伺服器根據一定的方法,以資料包的源地址、埠等資訊為引數計算出一個 cookie 值作為自己的 SYNACK 包的序列號,回覆 SYN+ACK 後,伺服器並不立即分配資源進行處理,等收到傳送方的 ACK 包後,重新根據資料包的源地址、埠計算該包中的確認序列號是否正確,如果正確則建立連線,否則丟棄該包。
SYN Proxy 防火牆:伺服器防火牆會對收到的每一個 SYN 報文進行代理和回應,並保持半連線。等傳送方將 ACK 包返回後,再重新構造 SYN 包發到伺服器,建立真正的 TCP 連線。
四次揮手斷開連線
- 資料傳輸結束之後,通訊雙方都可以主動發起斷開連線請求,這裡假定客戶端發起
- 客戶端傳送釋放連線報文,第一次揮手 (FIN=1,seq=u),傳送完畢後,客戶端進入 FIN_WAIT_1 狀態。
- 服務端傳送確認報文,第二次揮手 (ACK=1,ack=u+1,seq =v),傳送完畢後,伺服器端進入 CLOSE_WAIT 狀態,客戶端接收到這個確認包之後,進入 FIN_WAIT_2 狀態。
- 服務端傳送釋放連線報文,第三次揮手 (FIN=1,ACK1,seq=w,ack=u+1),傳送完畢後,伺服器端進入 LAST_ACK 狀態,等待來自客戶端的最後一個 ACK。
- 客戶端傳送確認報文,第四次揮手 (ACK=1,seq=u+1,ack=w+1),客戶端接收到來自伺服器端的關閉請求,傳送一個確認包,並進入 TIME_WAIT狀態,等待了某個固定時間(兩個最大段生命週期,2MSL,2 Maximum Segment Lifetime)之後,沒有收到伺服器端的 ACK ,認為伺服器端已經正常關閉連線,於是自己也關閉連線,進入 CLOSED 狀態。伺服器端接收到這個確認包之後,關閉連線,進入 CLOSED 狀態。
為什麼需要四次揮手
- 關閉連線時,客戶端向服務端傳送 FIN 時,僅僅表示客戶端不再傳送資料了但是還能接收資料。
- 服務端收到客戶端的 FIN 報文時,先回一個 ACK 應答報文,而服務端可能還有資料需要處理和傳送,等服務端不再傳送資料時,才傳送 FIN 報文給客戶端來表示同意現在關閉連線。
為什麼需要等待 2MSL, 才進入 CLOSED 關閉狀態
-
為了保證客戶端傳送的最後一個 ACK 報文段能夠到達服務端。 這個 ACK 報文段有可能丟失,因而使處在 LAST-ACK 狀態的服務端就收不到對已傳送的 FIN + ACK 報文段的確認。服務端會超時重傳這個 FIN+ACK 報文段,而客戶端就能在 2MSL 時間內(超時 + 1MSL 傳輸)收到這個重傳的 FIN+ACK 報文段。接著客戶端重傳一次確認,重新啟動 2MSL 計時器。最後,客戶端和伺服器都正常進入到 CLOSED 狀態。
-
防止已失效的連線請求報文段出現在本連線中。如果客戶端收到服務端的 FIN 報文之後立即關閉連線,但是此時服務端對應的埠並沒有關閉,如果客戶端在相同埠建立新的連線,可能會導致新連線收到舊連線殘留的資料包,導致不可預料的異常發生。。
保活計時器
除時間等待計時器外,TCP 還有一個保活計時器(keepalive timer)。
設想這樣的場景:客戶已主動與伺服器建立了 TCP 連線。但後來客戶端的主機突然發生故障。顯然,伺服器以後就不能再收到客戶端發來的資料。因此,應當有措施使伺服器不要再白白等待下去。這就需要使用保活計時器了。
伺服器每收到一次客戶端的資料,就重新設定保活計時器,時間的設定通常是兩個小時。若兩個小時都沒有收到客戶端的資料,服務端就傳送一個探測報文段,以後則每隔 75 秒鐘傳送一次。若連續傳送 10 個探測報文段後仍然無客戶端的響應,服務端就認為客戶端出了故障,接著就關閉這個連線。