更好閱讀體驗:《理解 TCP 和 UDP》— By Gitbook git
TCP 的整個交流過程能夠總結爲:先創建鏈接,而後傳輸數據,最後釋放連接。 服務器
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 所謂的「鏈接」,其實只不過是在通訊的雙方維護一個「鏈接狀態」,讓它看上去好像有鏈接同樣。 設計
TCP 鏈接的創建採用客戶服務器方式,主動發起鏈接創建的一方叫客戶端(Client),被動等待鏈接創建的一方叫服務器(Server)。 cdn
最初的時候,兩端都處於 CLOSED 的狀態,而後服務器打開了 TCP 服務,進入 LISTEN 狀態,監聽特定端口,等待客戶端的 TCP 請求。 blog
第一次握手:
客戶端主動打開鏈接,發送 TCP 報文,進行第一次握手,而後進入 SYN_SEND 狀態,等待服務器發回確認報文。
這時首部的同步位 SYN = 1,同時初始化一個序號 Sequence Number = J。
TCP 規定,SYN 報文段不能攜帶數據,但會消耗一個序號。 開發
第二次握手:
服務器收到了 SYN 報文,若是贊成創建鏈接,則向客戶端發送一個確認報文,而後服務器進入 SYN_RCVD 狀態。
這時首部的 SYN = 1,ACK = 1,而確認號 Acknowledgemt Number = J + 1,同時也爲本身初始化一個序號 Sequence Number = K。
這個報文一樣不攜帶數據。 get
第三次握手:
客戶端收到了服務器發過來的確認報文,還要向服務器給出確認,而後進入 ESTABLISHED 狀態。
這時首部的 SYN 再也不置爲 1,而 ACK = 1,確認號 Acknowledgemt Number = K + 1,序號 Sequence Number = J + 1。
第三次握手,通常會攜帶真正須要傳輸的數據,當服務器收到該數據報文的時候,就會一樣進入 ESTABLISHED 狀態。
此時,TCP 鏈接已經創建。
對於創建鏈接的三次握手,主要目的是初始化序號 Sequence Number,而且通訊的雙方都須要告知對方本身的初始化序號,因此這個過程也叫 SYN。
這個序號要做爲之後的數據通訊的序號,以保證應用層接收到的數據不會由於網絡上的傳輸問題而亂序,由於TCP 會用這個序號來拼接數據。
知道了 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 狀態。