TCP建立連接的相關問題


1.TCP三次握手過程和狀態變遷

  TCP是面向連接的協議,使用TCP前必須先建立連接,建立連接是通過三次握手進行的。

  

  (1)在一開始的時候,客戶端和服務端都是處於CLOSED狀態,先是服務端主動監聽某個端口,處於LISTEN狀態。

  (2)客戶端會隨機初始化序號(client_isn),將此序號置於TCP首部的序號字段中,同時把SYN標志位置為1,表示SYN報文。接着把第一個SYN報文發送給服務端,表示向服務端發起連接,該報文不包含應用層數據,之后客戶端處於SYN_SENT狀態。

  客戶端發送的SYN報文

  

   (3)服務端收到客戶端SYN報文后,首先服務端也隨機初始化自己的序號(server_isn),將此序號填入TCP首部的序號字段中,其次把TCP首部的確認應答號字段填入client_isn+1,接着把SYN和ACK標志位都置為1,最后把該報文發給客戶端,該報文也不包含應用層數據,之后服務端處於SYN-RCVD狀態。

  服務端發給客戶端SYN+ACK報文

  

   (4)客戶端收到服務端報文之后,還要向服務端回應最后一個應答報文,首先該應答報文TCP首部ACK標志位置為1,其次確認應答號字段填server_isn+1,最后把該報文發送給服務端,這次報文可以攜帶客戶端到服務器的數據,之后客戶端處於ESTABLISHED狀態。

  客戶端發給服務端的ACK報文

  

   (5)服務器收到客戶端的應答報文之后,也進入ESTABLISHED狀態。

  前兩次握手不可以攜帶數據,第三次握手是可以攜帶數據的。一旦完成三次握手,雙方就處於ESTABLISHED狀態,此時連接就已建立完成,客戶端和服務器端就可以相互發送數據了。

2.為什么是三次握手?而不是兩次或者四次?

  (1)三次握手才可以阻止歷史重復連接的初始化(主要原因)

  三次握手的首要原因是為了防止舊的重復連接初始化造成混亂。

  網絡環境是復雜的,由於網絡堵塞等原因,先發送的數據包並不一定是最先到達目標主機的,客戶端連續發送多次SYN建立連接的報文,在網絡堵塞的情況下:

  • 一個舊的SYN報文比最新的SYN報文早到達服務端;
  • 那么此時服務端就會回送一個SYN+ACK報文給客戶端;
  • 客戶端收到后根據自身上下文,判斷這是一個歷史連接(序列號過期或超時),那么客戶端就會發送一個RST報文給服務端,表示中止這一次連接。

  如果是兩次握手的話,就不能判斷當前連接是否是歷史連接,三次握手可以在客戶端准備發送第三次報文時,有足夠的上下文來判斷當前連接是否是歷史連接:

  1> 如果是歷史連接,則第三次握手發送的報文是RST報文,中止連接;

  2> 如果不是歷史連接,則第三次發送的報文是ACK報文,通信雙方會成功建立連接。

  所以,TCP使用三次握手建立連接的主要原因是防止歷史連接初始化了連接。  

  (2)三次握手才可以同步雙方的初始序列號

  TCP協議的通信雙方,都必須維護一個序列號,序列號是可靠傳輸的一個關鍵因素,它的作用是:

  • 接收方可以去除重復的數據;
  • 接收方根據數據包的序列號按序接收;
  • 可以標識發送出去的數據包中,哪些是已經被雙方收到的。

  當客戶端發送攜帶初始序列號的SYN報文時,需要服務端回一個ACK應答報文,表示客戶端的SYN報文已被成功接收,那當服務端發哦是能夠初始序列號給客戶端的時候,依然也要得到客戶端的應答回應,這樣一來一回,才能保證雙方的初始序列號能被可靠的同步。

  四次握手也能夠可靠的同步雙方的初始化序號,但由於第二步和第三步可以優化成一步,所以就成了三次握手。 而兩次握手只保證了一方的初始序列號能被對方成功接收,沒有辦法保證雙方的初始序列號都能被確認接收。

  

  (3)三次握手才可以避免資源浪費

  如果只有兩次握手,當客戶端的SYN請求連接在網絡中阻塞,客戶端沒有接收到ACK報文就會重新發送SYN,由於沒有第三次握手,服務器不清楚客戶端是否收到了自己發送的建立連接的ACK確認信號,所以每收到一個SYN就只能先主動建立一個連接,這樣服務器就會建立冗余的無效連接,造成不必要的資源浪費。

  也就是說,兩次握手在造成消息滯留的情況下,服務器重復接收無用的連接請求SYN報文,而造成重復分配資源。

  總結:  

  不使用兩次握手和四次握手的原因:

  • 兩次握手:無法防止歷史連接的建立,會造成雙方資源的浪費,也無法可靠的同步雙方序列號;
  • 四次握手:三次握手就已經理論上最少可靠連接建立,所以不需要使用更多的通信次數。

 3.為什么客戶端和服務端的初始序列號的ISN是不相同的?

  因為網絡中的報文會延遲、復制重發、有可能丟失,這樣會造成的不同連接之間產生相互影響,所以為了避免相互影響,客戶端和服務端的初始序列號是隨機且不同的。  

 4.初始序列號ISN是如何隨機產生的?

   最開始時ISN是基於時鍾的,每4毫秒+1,轉一圈要4.55個小時。

  RFC1948中提出了較好的初始化序列號ISN隨機生成算法。ISN=M+F(localhost, localport,remotehost, remoteport)。其中M是一個計時器,這個計時器每個4毫秒加1;F是一個Hash算法,根據源IP、目的IP、源端口、目標端口生成一個隨機數值,要保證Hash算法不能被外部輕易推算得出,用MD5算法是一個比較好的選擇。

 5.既然IP會分片,為什么TCP還需要MSS呢?

  MTU:一個網絡包的最大長度,以太網中一般是1500字節;

  MSS:除去IP和TCP頭部之后,一個網絡包所能容納的TCP數據的最大長度。

   

  如果TCP的整個報文(頭部+數據)交給IP進行分片的話:

  當IP層有一個超過MTU大小的數據(TCP頭部+TCP數據)要發送,那么IP層就要進行分片,把數據分片成若干片,保證每一個分片都小於MTU。把一份IP數據進行分片以后,由目標主機的IP層進行重新組裝后,再交給上一層傳輸層。但是如果一個IP分片丟失整個IP報文的所有分片都要重傳,因為IP層沒有超時重傳機制,它由傳輸層的TCP來負責超時和重傳。當接收方發現TCP報文(頭部+數據)的某一片丟失后,則不會響應ACK給對方,那么發送方的TCP在超時后,就會重發整個TCP報文(頭部+數據)。所以,由IP層進行分片傳輸的話是非常沒有效率的。

  為了達到最佳的傳輸效能,TCP協議在建立連接的時候通常需要協商雙方的MSS值,當TCP層發現數據超過MSS后,則就會先進行分片,當然由它形成的IP包的長度也就會大於MTU,自然也就不用IP分片。

  握手協商MSS:

  

   經過TCP分片后,如果一個TCP分片丟失后,進行重發時也是以MSS為單位,而不用重傳所有的分片,大大增大了重傳的效率。

6.什么是SYN攻擊?如何避免SYN攻擊?

  

   假設攻擊者短時間內偽造不同IP地址的SYN報文,服務端每接收到一個SYN報文,就進入SYN_RCVD狀態,但服務端發送出去的ACK+SYN報文無法得到未知IP的ACK應答,久而久之就會占滿服務端的SYN接收隊列(未連接隊列),使得服務器不能為正常用戶服務。

  避免攻擊的方式:

  (1)通過修改Linux內核參數,控制隊列大小和當隊列滿時應做什么處理。當網卡接收到數據包的速度大於內核處理的速度時,會有一個隊列保存這些數據包,超出處理能時,對新的SYN直接回RST,丟棄連接。

  (2)對Linux內核的SYN隊列(未完成連接建立)與Accept隊列(已完成連接的建立)的工作流程:

  

  •  當服務端接收到客戶端的SYN報文時,會將其加入到內核的SYN隊列中;
  • 發送SYN+ACK給客戶端,等待客戶端回應ACK報文;
  • 服務端接收到ACK報文后,從SYN隊列移除放入Accept隊列;
  • 應用通過調用accept()的socket接口,從Accept隊列取出的連接。

  

   如果應用程序過慢的話,就會導致Accept隊列被占滿。

  

   如果不斷受到SYN攻擊,就會導致SYN隊列被占滿。

  

  •  當SYN隊列滿之后,后續服務器收到SYN包,不再進入SYN隊列;
  • 計算出一個cookie值,再以SYN+ACK中的序列號返回客戶端;
  • 服務端接收到客戶端的應答報文時,服務器會檢查這個ACK包的合法性,如果合法,直接放入到Accept隊列;
  • 最后應用調用accept()中socket接口,從Accept隊列取出的連接。

參考文獻:https://mp.weixin.qq.com/s/ihDCVCI4jm24XDZ9bCTfqQ


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM