三次握手和四次揮手過程及常見問題

一、三次握手

1、連接過程

  1.1 被動打開:

    伺服器必須準備好接受外來的連接。這通常通過調用socket、bind和listen這三個函數來完成。伺服器狀態由closed轉換為listen狀態。

  1.2 主動打開:

    客服端通過調用connect發起主動打開。客戶TCP會發送一個SYN分解,包含本端的將在連接中發送的數據的初始序列號。

    通常SYN不攜帶數據,其所在IP數據報只含有一個IP首部、一個TCP首部及可能有的選項,通常包括MSS(最大分節大小)、

    窗口規模、時間戳等。客戶端狀態由closed狀態轉換為syn_sent狀態。

  1.3 伺服器確認客戶端SYN:

    服務端確認(ACK)客戶端的SYN,確認號為客戶端序列號+1,同時將自身的初始化序列號告知對方。伺服器狀態由listen狀態

    轉換為syn_recd狀態。

  1.4 客戶端確認伺服器SYN

    客戶端接收到伺服器的序列號後,發送確認(ACK)給服務端,確認號為客戶端序列號+1。自身狀態由syn_sent進入established狀態;

    服務端接收到客戶端發送的確認後,自身狀態由syn_recd轉換為established狀態。至此完成三次握手。

2、三次握手的目的

  2.1、確認初始化序列號

  2.2、確認MSS(最大分節大小)

  2.3、確認窗口規模

  2.4、時間戳:

3、accept發生在三次握手哪個階段?

  三次握手之後,tcp連接會加入到accept隊列。accept()會從隊列中取一個連接返回,若隊列為空,則阻塞。所以accept一定是發生在三次握手之後,connect只是發一個syn而已。

二、四次揮手

1、關閉過程

  2.1 客戶端主動關閉,向服務端發送FIN,自身狀態由established轉換為fin_wait_1。

  2.2 伺服器接收到FIN後,向客戶端發送ACK確認,自身由established轉換為close_wait狀態;客戶端接收到服務端的

    確認後,由fin_wait_1轉換為fin_wait_2狀態。

  2.3 伺服器在數據發送完畢之後向客戶端發送FIN,自身狀態由close_wait轉換為last_ask狀態,

  2.4 客戶端接收到服務端的FIN後,向服務端發送確認,自身進入time_wait狀態;服務端接收到ACK確認後狀態轉換為closed狀態

    客戶端在等待2MSL(報文最大生存時間)後,未收到服務端發送的重複FIN,那麼自身狀態轉換為closed狀態。至此完成四次揮手。

2、time_wait狀態存在的理由

  2.1 可靠的實現TCP的全雙工連接的終止

      因為要允許服務端確定收到客戶端發送的最終確認,如果服務端沒有收到確認,那麼就需要預留時間讓服務端重新發送的FIN能夠有效的

    到達客戶端,所以time_wait會持續2MSL(報文最大生存時間)。第一個MSL代表客戶端發送ACK到服務端的時間,如果服務端在這段時間沒有

    接收到客戶端發送的確認,那麼就重發FIN,那麼就需要第二個MSL。

  2.2 允許老的重複分節在網路中消逝。 

      為了避免新建的連接收到舊的網路分節而做出錯誤的回應。例如剛關閉了一個連接,過了一會後在相同的IP和埠建立了相同的連接,那麼當收到之後會

    做出不應該發生的回應。而2MSL的time_wait時間足以讓某個方向上的分組最多存活MSL秒即被丟棄。

三、一些問題

  1、為什麼需要三次握手?為什麼不是兩次或四次?

    為了實現全雙工可靠的數據傳輸,必須進行三次,三次握手的過程及雙方獲知對方的序列號、mss、窗口規模等資訊的過程,並需要確認對方真正獲知了這些資訊。

      如果只是兩次握手,那麼服務端就沒辦法確認客戶端是否接收到自身發送的序列號等資訊,那麼就會重新發送自身序列號等資訊。所以兩次握手至多只有連接發起

    方的起始序列號能被確認, 另一方選擇的序列號則得不到確認。

      四次握手沒有必要,三次握手已經能夠保證雙方都已經獲知了對方的資訊。

  2、為什麼需要四次揮手?

    主要是伺服器多了一個close_wait狀態,因為伺服器在接收到客戶端的FIN後,仍然允許服務端將未發送完的數據發送完畢,所以當服務端接收到FIN後,只是發送了

    一個確認,之後將後續需要發送的數據發送完畢之後在發送FIN。

  3、為什麼需要time_wait狀態?

    同上 二.2。

  4、time_wait狀態導致埠佔用完如何解決?

    原因:(1)高並發讓伺服器在短時間範圍內同時佔用大量埠,而埠只0~65535的範圍,有限。

       (2)短連接表示「業務處理+傳輸數據的時間 遠遠小於 TIMEWAIT超時的時間」的連接。

    後果:當大量的連接處於 time_wait 時,新建立 TCP 連接會出錯,address already in use : connect 異常。

    解決辦法:

       (1)允許 time_wait 狀態的 socket 被重用

       (2)縮減 time_wait 時間,設置為 1 MSL(即,2 mins)

  5、什麼是SYN攻擊?怎麼解決?

    半連接隊列(syn queue)(也稱 SYN 隊列)

      客戶端發送SYN包,服務端收到後回復SYN+ACK後,服務端進入SYN_RCVD狀態,這個時候的socket會放到半連接隊列。

    全連接隊列(也稱 accepet 隊列)

      經過三次握手的連接,將從半連接隊列中放到全連接隊列,即保存 ESTABLISHED 狀態的連接的隊列。

    原因:

      客戶端偽造大量不存在的IP地址向伺服器發送連接請求,這時候伺服器收到請求後進行回復,但是由於IP地址是不存在的,所以造成伺服器收不到回復,

    便會不斷重新發送回復請求,這時由於所有的重發都佔用了未連接隊列,導致正確的SYN請求由於隊列滿了而被丟棄,從而引起網路堵塞導致網路癱瘓。

    解決辦法:

      (1)查看linux默認的syn配置:

        

[root@web ~]# sysctl -a | grep _syn
  net.ipv4.tcp_max_syn_backlog = 1024  //SYN隊列的長度,加大SYN隊列長度可以容納更多等待連接的網路連接數。   net.ipv4.tcp_syncookies = 1    //是一個開關,是否打開SYN Cookie 功能,該功能可以防止部分SYN攻擊。   net.ipv4.tcp_synack_retries = 5   net.ipv4.tcp_syn_retries = 5    //tcp_synack_retries和tcp_syn_retries定義SYN 的重試連接次數,將默認的參數減小來控制SYN連接次數的盡量少。

     (2)修改配置

        

  #sysctl -w net.ipv4.tcp_max_syn_backlog=2048    //增大半連接隊列長度
    
  #sysctl -w net.ipv4.tcp_syncookies=1          //開啟syncookies

  #sysctl -w net.ipv4.tcp_synack_retries=3

  #sysctl -w net.ipv4.tcp_syn_retries=3        //減少超時重傳次數。