TCP 建立連線為什麼要握 3 次手?
上次已經說過,沒有協議,不成方圓,計算機之間的通訊更是依賴於協議。今天就重點分析一下 TCP 協議。
傳輸控制協議 TCP 是一種面向連線的、可靠的、基於位元組流的傳輸層通訊協議,由IETF的RFC 793定義。在簡化的計算機網路 OSI 模型中,它完成第四層傳輸層所指定的功能,使用者資料包協議(UDP)是同一層內另一個重要的傳輸協議。
先來複習一下 OSI 的七層模型。
TCP 工作在 OSI 中的第四層——Transport 層,IP 在第三層——Network 層,ARP 在第二層——Data Link 層;在第二層上的資料,我們把它叫 Frame,在第三層上的資料叫 Packet,第四層的資料叫 Segment。 同時,我們需要知道,資料從應用層發下來,會在每一層都會加上頭部資訊,進行封裝,然後再發送到資料接收端。
TCP 協議所涉及到的知識太多,沒有辦法兼顧,我主要想談談 3 次握手和 4 次揮手。TCP 的執行可以分為三個階段,建立連線、傳送資料、關閉連線。3 次握手 4 次揮手就對應著建立連線和關閉連線。
作業系統將 TCP 連線抽象為套接字表示的本地端點,作為程式設計介面給程式使用。在 TCP 連線的生命期內,本地端點要經歷一系列的狀態改變。我們經常會聽到面向 Socket 程式設計,這是後話了。
要想知道握手是怎樣進行了,我們先來看看 TCP 的報文結構。
有以下幾點需要說明
TCP 報文中沒有 IP 地址,有源埠和目的埠,IP 地址在網路層中的 Packet 中。
Sequence Number(序號),用來標識從 TCP 傳送端向 TCP 接收端傳送的資料位元組流,它表示在這個報文段中的的第一個資料位元組在資料流中的序號;通訊雙方要知道對方的初始化序號,這個號要作為以後的資料通訊的序號,以保證應用層接收到的資料不會因為網路上的傳輸的問題而亂序(TCP 會用這個序號來拼接資料)。
Acknowledgment Number(確認號)32 位確認序列號包含傳送確認的一端所期望收到的下一個序號,因此,確認序號應當是上次已成功收到資料位元組序號加 1。不過,只有當標誌位中的 ACK 標誌(下面介紹)為 1 時該確認序列號的欄位才有效。主要用來解決不丟包的問題。
SYN,表示同步序號,用來建立連線。SYN 標誌位和 ACK 標誌位搭配使用,當連線請求的時候,SYN=1,ACK=0;連線被響應的時候,SYN=1,ACK=1。
這個標誌的資料包經常被用來進行埠掃描。掃描者傳送一個只有 SYN 的資料包,如果對方主機響應了一個數據包回來 ,就表明這臺主機存在這個埠;但是由於這種掃描方式只是進行 TCP 三次握手的第一次握手,因此這種掃描的成功表示被掃描的機器不很安全,一臺安全的主機將會強制要求一個連線嚴格的進行 TCP 的三次握手。
ACK,TCP 協議規定,只有 ACK=1 時有效,也規定連線建立後所有傳送的報文的 ACK 必須為 1 。
FIN,表示傳送端已經達到資料末尾,也就是說雙方的資料傳送完成,沒有資料可以傳送了,傳送 FIN 標誌位的 TCP 資料包後,連線將被斷開。這個標誌的資料包也經常被用於進行埠掃描。
三次握手的過程,即建立一個 TCP 連線的過程,在 Socket 程式設計中,這一過程由客戶端執行 connect 來觸發,整個流程如上圖所示。
第一次握手:Client 將標誌位 SYN 置為1,隨機產生一個值 seq=x,並將該資料包傳送給 Server,Client 進入 SYN_SENT 狀態,等待 Server 確認。
第二次握手:Server 收到資料包後由標誌位 SYN=1 知道 Client 請求建立連線,Server 將標誌位 SYN 和 ACK 都置為1,ack=x+1,隨機產生一個值 seq=y,並將該資料包傳送給 Client 以確認連線請求,Server 進入 SYN_RCVD 狀態。
第三次握手:Client 收到確認後,檢查 ack 是否為 x+1,ACK 是否為1,如果正確則將標誌位 ACK 置為1,ack=y+1,並將該資料包傳送給 Server,Server 檢查 ack 是否為 y+1,ACK 是否為1,如果正確則連線建立成功,Client 和 Server 進入 ESTABLISHED 狀態,完成三次握手,隨後 Client 與 Server 之間可以開始傳輸資料了。
注意,不要搞混了 SYN、ACK 只是標誌位,seq 是序號,防止亂序,ack 是確認號,主要用來解決不丟包的問題。
四次揮手,即終止 TCP 連線,就是指斷開一個 TCP 連線時,需要客戶端和服務端總共傳送4個包以確認連線的斷開。在 Socket 程式設計中,這一過程由客戶端或服務端任一方執行 close 來觸發。
由於 TCP 連線是全雙工的,因此,每個方向都必須要單獨進行關閉,這一原則是當一方完成資料傳送任務後,傳送一個 FIN 來終止這一方向的連線,收到一個 FIN 只是意味著這一方向上沒有資料流動了,即不會再收到資料了,但是在這個 TCP 連線上仍然能夠傳送資料,直到對方也傳送了 FIN。首先進行關閉的一方將執行主動關閉,而另一方則執行被動關閉。
第一次揮手:主機1(可以使客戶端,也可以是伺服器端),設定Sequence Number 和 Acknowledgment Number,向主機2傳送一個FIN報文段;此時,主機1進入FIN_WAIT_1狀態;這表示主機1沒有資料要傳送給主機2了;
第二次揮手:主機2收到了主機1傳送的FIN報文段,向主機1回一個ACK報文段,Acknowledgment Number為Sequence Number加1;主機1進入FIN_WAIT_2狀態;主機2告訴主機1,我“同意”你的關閉請求;
第三次揮手:主機2向主機1傳送FIN報文段,請求關閉連線,同時主機2進入LAST_ACK狀態;
第四次揮手:主機1收到主機2傳送的FIN報文段,向主機2傳送ACK報文段,然後主機1進入TIME_WAIT狀態;主機2收到主機1的ACK報文段以後,就關閉連線;此時,主機1等待2MSL後依然沒有收到回覆,則證明 Server 端已正常關閉,那好,主機1也可以關閉連線了。
為什麼建立連線需要 3 次握手 ?
因為,TCP 協議要保證兩端資料的可靠傳輸,最少就要採取 3 次互動。三次是保證雙方互相明確對方能收能發的最低值。下面有個生活中的例子可以感受一下。
TCP之所以使用三次握手,完全是一種為了解決“兩軍問題”所採用的折衷的設計。所謂“兩軍問題”,就是紅軍想告訴藍軍明天下午一起對敵開火,那麼紅軍會派信使 1 號跑過去告訴藍軍,藍軍收到訊息再派信使 2 號告訴紅軍收到,注意,這時藍軍並不知道紅軍是否收到藍軍的回覆。因此需要紅軍收到回覆再派信使 3 號告訴藍軍收到回覆,而此時紅軍也不知道藍軍是否收到回覆,因此藍軍收到信使 3 號的訊息再派信使 4 號…
還有一個秒懂例子,看到這個真的笑死我了。但是這個例子不嚴謹哦,權當一樂就好。
三次握手:“喂,你聽得到嗎?”“我聽得到呀,你聽得到我嗎?”“我能聽到你,今天balabala……”
兩次握手:“喂,你聽得到嗎?”“我聽得到呀”“喂喂,你聽得到嗎?”“草,我聽得到呀!!!!”“你TM能不能聽到我講話啊!!喂!”“……”
四次握手:“喂,你聽得到嗎?”“我聽得到呀,你聽得到我嗎?”“我能聽到你,你能聽到我嗎?”“……不想跟傻逼說話”
所以說,握手次數要保持剛剛好,2 次不夠,4 次多了。
SYN 攻擊
SYN 攻擊指的是,攻擊客戶端在短時間內偽造大量不存在的 IP 地址,向伺服器不斷地傳送 SYN 包,伺服器回覆確認包,並等待客戶的確認。由於源地址是不存在的,伺服器需要不斷的重發直至超時,這些偽造的 SYN 包將長時間佔用未連線佇列,正常的 SYN 請求被丟棄,導致目標系統執行緩慢,嚴重者會引起網路堵塞甚至系統癱瘓。SYN 攻擊是一種典型的 DoS/DDoS 攻擊。
檢測 SYN 攻擊非常的方便,當你在伺服器上看到大量的半連線狀態時,特別是源IP地址是隨機的,基本上可以斷定這是一次SYN攻擊。
如何防禦 SYN 攻擊 ?
SYN 攻擊不能完全被阻止,除非將 TCP 協議重新設計。我們所做的是儘可能的減輕 SYN 攻擊的危害,常見的防禦 SYN 攻擊的方法有如下幾種:
縮短超時(SYN Timeout)時間
增加最大半連線數
過濾閘道器防護
SYN cookies 技術