TCP 三次握手和四次揮手圖解(有限狀態機)

傳輸控制協議(TCP,Transmission Control Protocol)是一種面向鏈接的、可靠的、基於字節流的傳輸層通訊協議,由 IETF 的 RFC 793 定義,是爲了在不可靠的互聯網絡上提供可靠的端到端字節流而專門設計的一個傳輸協議。 面試

互聯網絡與單個網絡有很大的不一樣,由於互聯網絡的不一樣部分可能有大相徑庭的拓撲結構、帶寬、延遲、數據包大小和其餘參數。TCP 的設計目標是可以動態地適應互聯網絡的這些特性,並且具有面對各類故障時的健壯性。 服務器

應用層向 TCP 層發送用於網間傳輸的、用 8 位字節表示的數據流,而後 TCP 把數據流分區成適當長度的報文段(一般受該計算機鏈接的網絡的數據鏈路層的最大傳輸單元(MTU)的限制)。以後 TCP 把結果包傳給 IP 層,由它來經過網絡將包傳送給接收端實體的 TCP 層。TCP 爲了保證不發生丟包,就給每一個包一個序號,同時序號也保證了傳送到接收端實體的包的按序接收。而後接收端實體對已成功收到的包發回一個相應的確認(ACK);若是發送端實體在合理的往返時延(RTT)內未收到確認,那麼對應的數據包就被假設爲已丟失將會被進行重傳。TCP 用一個校驗和函數來檢驗數據是否有錯誤;在發送和接收時都要計算校驗和。 網絡

TCP 消息頭

 

下面對每一個字段含義進行解釋併發

  • 源、目標端口號字段:佔 16 比特 (2 字節)。TCP 協議經過使用」端口」來標識源端和目標端的應用進程。端口號可使用0到65535之間的任何數字。在收到服務請求時,操做系統動態地爲客戶端的應用程序分配端口號。在服務器端,每種服務在」衆所周知的端口」(Well-Know Port)爲用戶提供服務。socket

  • 順序號字段:佔 32 比特。用來標識從 TCP 源端向 TCP 目標端發送的數據字節流,它表示在這個報文段中的第一個數據字節。在TCP傳送的流中,每個字節一個順序號。e.g.若是一個 TCP 報文段的序號爲 301,它攜帶了 100 字節的數據,就表示這 100 個字節的數據的字節序號範圍是 [301, 400],該報文段攜帶的第一個字節序號是 301,最後一個字節序號是 400。因此順序號號確保了TCP傳輸的有序性。函數

  • 確認號字段:佔 32 比特。只有 ACK 標誌爲1時,確認號字段纔有效。它包含目標端所指望收到源端的下一個數據字節。好比創建鏈接時,SYN 報文的 ACK 標誌位爲 0。spa

  • 頭部長度字段:佔 4 比特。給出頭部佔 32 比特的數目。沒有任何選項字段的 TCP 頭部長度爲 20 字節;最多能夠有 60 字節的 TCP 頭部。4位首部長度字段所能表示的最大值爲1111,轉化爲10進製爲15,15*32/8 = 60,故報頭最大長度爲60字節操作系統

  • 標誌位字段(U、A、P、R、S、F):佔 6 比特。各比特的含義以下:窗口大小字段:佔 16 比特。此字段用來進行流量控制。單位爲字節數,這個值是本機指望一次接收的字節數。.net

    • URG:緊急指針標誌,爲 1 時表示緊急指針有效,爲 0 則忽略緊急指針。設計

    • ACK:確認序號標誌,爲 1 時表示確認號有效,爲 0 表示報文中不含確認信息,忽略確認號字段。

    • PSH:push 標誌,爲 1 表示是帶有 push 標誌的數據,指示接收方在接收到該報文段之後,應儘快將這個報文段交給應用程序,而不是在緩衝區排隊。

    • RST:重置鏈接標誌,用於重置因爲主機崩潰或其餘緣由而出現錯誤的鏈接。或者用於拒絕非法的報文段和拒絕鏈接請求。

    • SYN:同步序號,用於創建鏈接過程,在鏈接請求中,SYN = 1 和 ACK = 0 表示該數據段沒有使用捎帶的確認域,而鏈接應答捎帶一個確認,即 SYN = 1 和 ACK = 1。

    • FIN:finish 標誌,用於釋放鏈接,爲1時表示發送方已經沒有數據發送了,即關閉本方數據流。

  • TCP 校驗和字段:佔 16 比特。對整個 TCP 報文段,即 TCP 頭部和 TCP 數據進行校驗和計算,並由目標端進行驗證。

  • 緊急指針字段:佔 16 比特。它是一個偏移量,和序號字段中的值相加表示緊急數據最後一個字節的序號。
  • 選項和填充:最多見的可選字段是最長報文大小,又稱爲 MSS(Maximum Segment Size),每一個鏈接方一般都在通訊的第一個報文段(爲創建鏈接而設置SYN標誌爲1的那個段)中指明這個選項,它表示本端所能接受的最大報文段的長度。選項長度不必定是32位的整數倍,因此要加填充位,即在這個字段中加入額外的零,以保證TCP頭是32的整數倍。
  • 數據部分 TCP 報文段中的數據部分是可選的。在一個鏈接創建和一個鏈接終止時,雙方交換的報文段僅有 TCP 首部。若是一方沒有數據要發送,也使用沒有任何數據的首部來確認收到的數據。在處理超時的許多狀況中,也會發送不帶任何數據的報文段。

TCP 三次握手

TCP是一個面向鏈接的協議,不管哪一方向另外一方發送數據以前,都必須先在雙方之間創建一條鏈接,創建一條鏈接有如下過程。

  • 第一次握手:創建鏈接時,客戶端發送 syn 包(syn=x)到服務器,並進入SYN_SENT狀態,等待服務器確認;SYN:同步序列編號(Synchronize Sequence Numbers)。

  • 第二次握手:服務器收到 syn 包,必須確認客戶的 SYN(ack=x+1),同時本身也發送一個 SYN 包(syn=y),即 SYN+ACK 包,此時服務器進入 SYN_RECV 狀態;

  • 第三次握手:客戶端收到服務器的 SYN + ACK 包,向服務器發送確認包 ACK(ack=y+1),此包發送完畢,客戶端和服務器進入 ESTABLISHED(TCP鏈接成功)狀態,完成三次握手。

這三個報文段完成鏈接的創建,這個過程成爲三次握手。

SYN 攻擊

在三次握手過程當中,Server 發送 SYN-ACK 以後,收到 Client 的 ACK 以前的 TCP 鏈接稱爲半鏈接(half-open connect),此時 Server 處於 SYN_RCVD 狀態,當收到 ACK 後,Server 轉入 ESTABLISHED 狀態。

SYN 攻擊就是 Client 在短期內僞造大量不存在的 IP 地址,並向 Server 不斷地發送 SYN 包,Server 回覆確認包,並等待 Client 的確認,因爲源地址是不存在的,所以,Server 須要不斷重發直至超時,這些僞造的 SYN 包將長時間佔用未鏈接隊列,致使正常的 SYN 請求由於隊列滿而被丟棄,從而引發網絡堵塞甚至系統癱瘓。

SYN 攻擊時一種典型的 DDOS 攻擊,檢測 SYN 攻擊的方式很是簡單,即當 Server 上有大量半鏈接狀態且源 IP 地址是隨機的,則能夠判定遭到 SYN 攻擊了。  

解決方案:

  • 一、無效鏈接監視釋放

  • 二、延緩TCB分配方法

當開放了一個 TCP 端口後,該端口就處於 Listening 狀態,不停地監視發到該端口的 Syn 報文,

一旦接收到 Client 發來的 Syn 報文,就須要爲該請求分配一個 TCB(Transmission Control Block),並返回一個 SYN ACK 命令,當即轉爲 SYN-RECEIVED 即半開鏈接狀態。

一般一個 TCB 至少須要 280 個字節,在某些操做系統中 TCB 甚至須要 1300 個字節,而某些操做系統在 SOCK 的實現上最多可開啓 512 個半開鏈接。

消耗服務器資源主要是由於當 SYN 數據報文一到達,系統當即分配 TCB,從而佔用了資源。

解決:

收到 SYN 數據報文時不急於去分配TCB,而是先回應一個SYN ACK報文,並在一個專用HASH表(Cache)中保存這種半開鏈接信息,直到收到正確的迴應ACK報文再分配TCB。

TCP終止鏈接過程(四次揮手)

  1. 客戶端進程發出鏈接釋放報文,而且中止發送數據。釋放數據報文首部,FIN=1,其序列號爲 seq=u(等於前面已經傳送過來的數據的最後一個字節的序號加 1),此時,客戶端進入 FIN-WAIT-1(終止等待1)狀態。 TCP 規定,FIN報文段即便不攜帶數據,也要消耗一個序號。

  2. 服務器收到鏈接釋放報文,發出確認報文,ACK=1,ack=u+1,而且帶上本身的序列號 seq=v,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器通知高層的應用進程,客戶端向服務器的方向就釋放了,這時候處於半關閉狀態,即客戶端已經沒有數據要發送了,可是服務器若發送數據,客戶端依然要接受。這個狀態還要持續一段時間,也就是整個 CLOSE-WAIT 狀態持續的時間。

  3. 客戶端收到服務器的確認請求後,此時,客戶端就進入 FIN-WAIT-2(終止等待2)狀態,等待服務器發送鏈接釋放報文(在這以前還須要接受服務器發送的最後的數據)。

  4. 服務器將最後的數據發送完畢後,就向客戶端發送鏈接釋放報文,FIN=1,ack=u+1,因爲在半關閉狀態,服務器極可能又發送了一些數據,假定此時的序列號爲 seq=w,此時,服務器就進入了 LAST-ACK(最後確認)狀態,等待客戶端的確認。

  5. 客戶端收到服務器的鏈接釋放報文後,必須發出確認,ACK=1,ack=w+1,而本身的序列號是 seq=u+1,此時,客戶端就進入了 TIME-WAIT(時間等待)狀態。注意此時 TCP鏈接尚未釋放,必須通過 2∗∗MSL(最長報文段壽命)的時間後,當客戶端撤銷相應的TCB後,才進入 CLOSED 狀態。

  6. 服務器只要收到了客戶端發出的確認,當即進入 CLOSED 狀態。一樣,撤銷 TCB 後,就結束了此次的 TCP 鏈接。能夠看到,服務器結束 TCP 鏈接的時間要比客戶端早一些。

TCP 狀態機

 

上圖是 TCP 的狀態機。

  1. CLOSED:狀態時初始狀態。

  2. LISTEN:被動打開,服務器端的 狀態變爲 LISTEN (監聽)。被動打開的概念:鏈接的一端的應用程序通知操做系統,但願創建一個傳入的鏈接。這時候操做系統爲鏈接的這一端創建一個鏈接。與之對應的是主動鏈接:應用程序經過主動打開請求來告訴操做系統創建一個鏈接。

  3. SYNRECVD:服務器端收到 SYN 後,狀態爲 SYN;發送 SYN ACK;

  4. SYN_SENTY:應用程序發送 SYN 後,狀態爲 SYN_SENT;

  5. ESTABLISHED:SYNRECVD 收到 ACK 後,狀態爲 ESTABLISHED; SYN_SENT 在收到 SYN ACK,發送 ACK,狀態爲 ESTABLISHED;

  6. CLOSE_WAIT:服務器端在收到 FIN 後,發送 ACK,狀態爲 CLOSE_WAIT;若是此時服務器端還有數據須要發送,那麼就發送,直到數據發送完畢;此時,服務器端發送FIN,狀態變爲 LAST_ACK;

  7. FIN_WAIT_1:應用程序端發送 FIN,準備斷開 TCP 鏈接;狀態從 ESTABLISHED -> FIN_WAIT_1;

  8. FIN_WAIT_2:應用程序端只收到服務器端得 ACK 信號,並無收到FIN信號;說明服務器端還有數據傳輸,那麼此時爲半鏈接;

  9. TIME_WAIT:有兩種方式進入該狀態:

    • FIN_WAIT_1進入:此時應用程序端口收到 FIN+ACK(而不是像 FIN_WAIT_2 那樣只收到 ACK,說明數據已經發送完畢)並向服務器端口發送 ACK;

    • FIN_WAIT_2進入:此時應用程序端口收到了 FIN,而後向服務器端發送 ACK;TIME_WAIT 是爲了實現 TCP 全雙工鏈接的可靠性關閉,用來重發可能丟失的 ACK 報文;須要持續 2 個 MSL (最大報文生存時間):假設應用程序端口在進入 TIME_WAIT後,2 個 MSL 時間內並無收到FIN,說明應用程序最後發出的 ACK 已經收到了;不然,會在 2 個 MSL 內在此收到ACK報文;

客戶端應用程序的狀態遷移圖

客戶端的狀態能夠用以下的流程來表示:

CLOSED -> SYN_SENT -> ESTABLISHED -> FIN_WAIT_1 -> FIN_WAIT_2 -> TIME_WAIT -> CLOSED

以上流程是在程序正常的狀況下應該有的流程,從書中的圖中能夠看到,在創建鏈接時,當客戶端收到 SYN 報文的 ACK 之後,客戶端就打開了數據交互地鏈接。而結束鏈接則一般是客戶端主動結束的,客戶端結束應用程序之後,須要經歷 FIN_WAIT_1,FIN_WAIT_2 等狀態,這些狀態的遷移就是前面提到的結束鏈接的四次握手。

服務器的狀態遷移圖

服務器的狀態能夠用以下的流程來表示:

CLOSED -> LISTEN -> SYN 收到 -> ESTABLISHED -> CLOSE_WAIT -> LAST_ACK -> CLOSED

在創建鏈接的時候,服務器端是在第三次握手以後才進入數據交互狀態,而關閉鏈接則是在關閉鏈接的第二次握手之後(注意不是第四次)。而關閉之後還要等待客戶端給出最後的ACK 包才能進入初始的狀態。

其餘狀態遷移

書中的圖還有一些其餘的狀態遷移,這些狀態遷移針對服務器和客戶端兩方面的總結以下

  • LISTEN -> SYN_SENT,對於這個解釋就很簡單了,服務器有時候也要打開鏈接的嘛。

  • SYN_SENT -> SYN 收到,服務器和客戶端在 SYN_SENT 狀態下若是收到 SYN 數據報,則都須要發送 SYN 的 ACK 數據報並把本身的狀態調整到 SYN 收到狀態,準備進入ESTABLISHED

  • SYN_SENT -> CLOSED,在發送超時的狀況下,會返回到 CLOSED 狀態。

  • SYN_收到 -> LISTEN,若是受到 RST 包,會返回到 LISTEN 狀態。

  • SYN_收到 -> FIN_WAIT_1,這個遷移是說,能夠不用到 ESTABLISHED 狀態,而能夠直接跳轉到 FIN_WAIT_1 狀態並等待關閉。

2 MSL 等待狀態

書中給的圖裏面,有一個 TIME_WAIT 等待狀態,這個狀態又叫作 2 MSL 狀態,說的是在 TIME_WAIT2 發送了最後一個 ACK 數據報之後,要進入 TIME_WAIT 狀態,這個狀態是防止最後一次握手的數據報沒有傳送到對方那裏而準備的(注意這不是四次握手,這是第四次握手的保險狀態)。這個狀態在很大程度上保證了雙方均可以正常結束,可是,問題也來了。

因爲插口的 2MSL 狀態(插口是IP和端口對的意思,socket),使得應用程序在 2 MSL 時間內是沒法再次使用同一個插口的,對於客戶程序還好一些,可是對於服務程序,例如httpd,它老是要使用同一個端口來進行服務,而在 2 MSL 時間內,啓動 httpd 就會出現錯誤(插口被使用)。爲了不這個錯誤,服務器給出了一個平靜時間的概念,這是說在 2 MSL 時間內,雖然能夠從新啓動服務器,可是這個服務器仍是要平靜的等待 2 MSL 時間的過去才能進行下一次鏈接。

FIN_WAIT_2 狀態

這就是著名的半關閉的狀態了,這是在關閉鏈接時,客戶端和服務器兩次握手以後的狀態。在這個狀態下,應用程序還有接受數據的能力,可是已經沒法發送數據,可是也有一種多是,客戶端一直處於 FIN_WAIT_2 狀態,而服務器則一直處於 WAIT_CLOSE 狀態,而直到應用層來決定關閉這個狀態。

RST,同時打開和同時關閉

RST 是另外一種關閉鏈接的方式,應用程序應該能夠判斷 RST 包的真實性,便是否爲異常停止。而同時打開和同時關閉則是兩種特殊的 TCP 狀態,發生的機率很小。

常見面試題

【問題1】爲何鏈接的時候是三次握手,關閉的時候倒是四次握手?

答:由於當 Server 端收到 Client 端的 SYN 鏈接請求報文後,能夠直接發送 SYN+ACK 報文。其中 ACK 報文是用來應答的,SYN 報文是用來同步的。可是關閉鏈接時,當 Server 端收到 FIN 報文時,極可能並不會當即關閉 SOCKET,因此只能先回復一個 ACK 報文,告訴 Client 端,"你發的 FIN 報文我收到了"。只有等到我 Server 端全部的報文都發送完了,我才能發送 FIN 報文,所以不能一塊兒發送。故須要四步握手。

【問題2】爲何TIME_WAIT狀態須要通過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?

雖然按道理,四個報文都發送完畢,咱們能夠直接進入 CLOSE 狀態了,可是咱們必須假象網絡是不可靠的,有能夠最後一個 ACK 丟失。因此TIME_WAIT狀態就是用來重發可能丟失的 ACK 報文。在 Client 發送出最後的 ACK 回覆,但該 ACK 可能丟失。Server 若是沒有收到 ACK,將不斷重複發送 FIN 片斷。因此 Client 不能當即關閉,它必須確認 Server 接收到了該ACK。Client 會在發送出ACK 以後進入到 TIME_WAIT 狀態。Client 會設置一個計時器,等待 2MSL 的時間。若是在該時間內再次收到FIN,那麼 Client 會重發 ACK 並再次等待2 MSL。所謂的 2 MSL 是兩倍的 MSL(Maximum Segment Lifetime)。MSL 指一個片斷在網絡中最大的存活時間,2MSL 就是一個發送和一個回覆所需的最大時間。若是直到 2 MSL,Client 都沒有再次收到 FIN,那麼 Client 推斷 ACK 已經被成功接收,則結束 TCP 鏈接。

【問題3】爲何不能用兩次握手進行鏈接?

3 次握手完成兩個重要的功能,既要雙方作好發送數據的準備工做(雙方都知道彼此已準備好),也要容許雙方就初始序列號進行協商,這個序列號在握手過程當中被髮送和確認。

如今把三次握手改爲僅須要兩次握手,死鎖是可能發生的。做爲例子,考慮計算機 S 和 C 之間的通訊,假定 C 給 S 發送一個鏈接請求分組,S 收到了這個分組,併發 送了確認應答分組。按照兩次握手的協定,S 認爲鏈接已經成功地創建了,能夠開始發送數據分組。但是,C 在 S 的應答分組在傳輸中被丟失的狀況下,將不知道 S 是否已準備好,不知道 S 創建什麼樣的序列號,C 甚至懷疑 S 是否收到本身的鏈接請求分組。在這種狀況下,C 認爲鏈接還未創建成功,將忽略 S 發來的任何數據分組,只等待鏈接確認應答分組。而 S 在發出的分組超時後,重複發送一樣的分組。這樣就造成了死鎖。

【問題4】若是已經創建了鏈接,可是客戶端忽然出現故障了怎麼辦?

TCP 還設有一個保活計時器,顯然,客戶端若是出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求後都會從新復位這個計時器,時間一般是設置爲 2 小時,若兩小時尚未收到客戶端的任何數據,服務器就會發送一個探測報文段,之後每隔 75 秒鐘發送一次。若一連發送 10 個探測報文仍然沒反應,服務器就認爲客戶端出了故障,接着就關閉鏈接

參考文章

https://blog.csdn.net/whuslei/article/details/6667471

https://blog.csdn.net/qq_38950316/article/details/81087809

https://www.sohu.com/a/321838267_495675

https://www.iteye.com/blog/uule-2213562

相關文章
相關標籤/搜索