理解 TCP(三):連線的建立和釋放

JerryC發表於2017-03-02

更好閱讀體驗:《理解 TCP 和 UDP》— By Gitbook

TCP 的整個交流過程可以總結為:先建立連線,然後傳輸資料,最後釋放連結。

理解 TCP(三):連線的建立和釋放
三次握手和四次揮手.png

三次握手,建立連線

TCP 連線建立要解決的首要問題就是:要使每一方能夠確知對方的存在。

三次握手就像,在一個黑暗的森林,你知道前方十點鐘方向好像有人。
你喊了一句:Hello?I'am JerryC,Who are you?
對面回了一句:Hi! I'am David, and nice to meet you!
然後你回了一句:Nice to meet you too!
......(自此,你們才算真正認識了雙方,開始了後面省略3000字的談話)

所以說,兩個人需要交朋友(兩個端點需要建立連線),至少需要三次的通話(握手)

其實,網路上的傳輸是沒有連線的,TCP 也是一樣的。
而 TCP 所謂的「連線」,其實只不過是在通訊的雙方維護一個「連線狀態」,讓它看上去好像有連線一樣。

連線建立過程

TCP 連線的建立採用客戶伺服器方式,主動發起連線建立的一方叫客戶端(Client),被動等待連線建立的一方叫伺服器(Server)

最初的時候,兩端都處於 CLOSED 的狀態,然後伺服器開啟了 TCP 服務,進入 LISTEN 狀態,監聽特定埠,等待客戶端的 TCP 請求。

第一次握手
客戶端主動開啟連線,傳送 TCP 報文,進行第一次握手,然後進入 SYN_SEND 狀態,等待伺服器發回確認報文。
這時首部的同步位 SYN = 1,同時初始化一個序號 Sequence Number = J。
TCP 規定,SYN 報文段不能攜帶資料,但會消耗一個序號。

第二次握手
伺服器收到了 SYN 報文,如果同意建立連線,則向客戶端傳送一個確認報文,然後伺服器進入 SYN_RCVD 狀態。
這時首部的 SYN = 1,ACK = 1,而確認號 Acknowledgemt Number = J + 1,同時也為自己初始化一個序號 Sequence Number = K。
這個報文同樣不攜帶資料。

第三次握手
客戶端收到了伺服器發過來的確認報文,還要向伺服器給出確認,然後進入 ESTABLISHED 狀態。
這時首部的 SYN 不再置為 1,而 ACK = 1,確認號 Acknowledgemt Number = K + 1,序號 Sequence Number = J + 1。
第三次握手,一般會攜帶真正需要傳輸的資料,當伺服器收到該資料包文的時候,就會同樣進入 ESTABLISHED 狀態。
此時,TCP 連線已經建立。

對於建立連線的三次握手,主要目的是初始化序號 Sequence Number,並且通訊的雙方都需要告知對方自己的初始化序號,所以這個過程也叫 SYN。
這個序號要作為以後的資料通訊的序號,以保證應用層接收到的資料不會因為網路上的傳輸問題而亂序,因為TCP 會用這個序號來拼接資料。

利用連線設計缺陷實施 TCP Flood 攻擊

知道了 TCP 建立一個連線,需要進行三次握手。
但如果你開始思考「三次握手的必要性」的時候,就會知道,其實網路是很複雜的,一個資訊在途中丟失的可能性是有的。
如果資料丟失了,那麼,就需要重新傳送,這時候就要知道資料是否真的送達了。
這就是三次握手的必要性。
但是再向深一層思考,你給我發資訊,我收到了,我回復,因為我是君子。
如果是小人,你給我發資訊,我就算收到了,我也不回覆,你就一直等我著我的回覆。
那麼很多小人都這樣做,你就要一直記住你在等待著小人1號、小人2號、小人3號......直到你的腦容量爆棚,燒壞腦袋。
黑客就是利用這樣的設計缺陷,實施 TCP Flood 攻擊,屬於 DDOS 攻擊的一種。
想了解更多 SYN Flood 攻擊請看:SYN flood - wiki

四次揮手,釋放連線

TCP 有一個特別的概念叫做半關閉,這個概念是說,TCP 的連線是全雙工(可以同時傳送和接收)的連線,因此在關閉連線的時候,必須關閉傳送和接收兩個方向上的連線。
客戶端給伺服器傳送一個攜帶 FIN 的 TCP 結束報文段,然後伺服器返回給客戶端一個 確認報文段,同時傳送一個 結束報文段,當客戶端回覆一個 確認報文段 之後,連線就結束了。

釋放連線過程

在結束之前,通訊雙方都是處於 ESTABLISHED 狀態,然後其中一方主動斷開連線。
下面假如客戶端先主動斷開連線。

第一次揮手:
客戶端向伺服器傳送結束報文段,然後進入 FIN_WAIT_1 狀態。
此報文段 FIN = 1, Sequence Number = M。

第二次揮手:
服務端收到客戶端的結束報文段,然後傳送確認報文段,進入 CLOSE_WAIT 狀態。
此報文段 ACK = 1, Sequence Number = M + 1。

客戶端收到該報文,會進入 FIN_WAIT_2 狀態。

第三次揮手:
同時服務端向客戶端傳送結束報文段,然後進入 LAST_ACK 狀態。
此報文段 FIN = 1,Sequence Number = N。

第四次揮手:
客戶端收到服務端的結束報文段,然後傳送確認報文段,進入 TIME_WAIT 狀態,經過 2MSL 之後,自動進入 CLOSED 狀態。
此報文段 ACK = 1, Sequence Number = N + 1。

服務端收到該報文之後,進入 CLOSED 狀態。

關於 TIME_WAIT 過渡到 CLOSED 狀態說明
TIME_WAIT 進入 CLOSED 需要經過 2MSL,其中 MSL 就叫做 最長報文段壽命(Maxinum Segment Lifetime),根據 RFC 793 建議該值這是為 2 分鐘,也就是說需要經過 4 分鐘,才進入 CLOSED 狀態。

參考

《後臺開發 核心技術與應用實踐》
《計算機網路》

相關文章