三次握手和四次揮手

1. TCP簡介及報文格式

1.1 簡介

TCP(Transmission Control Protocol)傳輸控制協議是一種面向連接的、可靠的、基於位元組流的傳輸層協議。

1.2 報文格式

TCP報文格式

重要欄位:

  1. 埠號:16位,用來標識同一台電腦的不同的應用進程。
  • 1)源埠:源埠和IP地址的作用是標識報文的返回地址。

  • 2)目的埠:埠指明接收方電腦上的應用程式介面。

  1. 序號:Seq序號,32位,一次TCP通訊 (從TCP連接建立到斷開) 過程中某一個傳輸方向上的位元組流的每個位元組的編號。

  2. 確認序號:ACK序號,32位,用作對另一方發送來的TCP報文段的響應。只有ACK標誌位為1時,確認序號欄位才有效,其值是收到的TCP報文段的序號值加1。

  3. 標誌位:共6個,即URG、ACK、PSH、RST、SYN、FIN 等。

    標誌位 名稱 具體含義
    URG 緊急標誌 緊急指針(urgent pointer)有效
    ACK 確認標誌 確認序號有效
    PSH 推標誌 接收方應儘快將報文交給應用層
    RST 複位標誌 重置連接
    SYN 同步標誌 發起一個新連接
    FIN 結束標誌 釋放一個連接

2. 三次握手(Three-way Handshake)

2.1 含義

建立TCP連接時,客戶端和伺服器總共發送3個包。

2.2 三次握手過程

三次握手

  1. 客戶端發送syn報文。SYN標誌為1,指明客戶端打算連接的伺服器的埠,以及初始序號X,保存在包頭的序列號(Sequence Number)欄位里。
  2. 伺服器發回確認syn+ack報文。SYN標誌為1,確認序號(Acknowledgement Number)為客戶端的序列號加1,即X+1,並設置發送序號為Y。
  3. 客戶端再次發送ack報文。 SYN標誌位為0,確認序號(Acknowledgement Number)為伺服器的序列號加1,即Y+1,並設置發送序號為Z。

問題1:為什麼兩次握手不行?

三次握手是為了讓雙方驗證各自接收能力和發送能力。

  • 1st:A發送SYN給B,B接收到。這裡B能確認A的發送能力和B的接收能力。

  • 2nd:B發送SYNACK給A,A收到。這裡A能確認A的接收能力和B的發送能力。此外,A收到SYNACK,說明前面A的SYN成功到達B,也能確認A自己的發送能力和B的接收能力。至此,A已經確認雙方各自發送能力和接收能力OK,因此轉為ESTABLISHED狀態。

  • 3rd:A發送ACK到B,B接收。這裡B能確認A的發送能力和B的接收能力。由於B能收到ACK,說明前面發送的SYNACK已經被接受了,說明A的接收能力和B的發送能力正常。

若使用兩次握手,就不能確認上述四種能力,可能有問題。

  • 假定A發的SYN報文沒消失,而是在某網路節點長時間滯留了,以至於到連接釋放後的某個時間才到達B。

  • 本來這是一個早已失效的報文段,但B收到此失效連接請求報文段後,卻誤以為是A又發出一次新的連接請求,於是B就發出確認報文段,同時建立連接。

  • 由於現在A並沒有發出建立連接請求,因此不理睬B的SYNACK報文,也不會向B發送數據,但B卻以為新連接已經建立,並一直等待A發來的數據,B的許多資源被白白浪費。

問題2:三次握手過程中可以攜帶數據嗎?

前兩次不行,第三次可以攜帶。 假如第一次可以,如果有人惡意攻擊伺服器,那他在第一次SYN 報文中放入大量數據。因為攻擊者根本不理會伺服器的接收、發送能力是否正常,只是瘋狂重複發 SYN 報文,這會讓伺服器花費很多記憶體與時間來接收這些報文。也就是說,第一次握手不能放數據,1個簡單原因伺服器會更容易受到攻擊。而對於第三次,此時客戶端處於 ESTABLISHED 狀態,已經建立起連接,知道伺服器接收與發送能力正常,所以攜帶數據也沒毛病。

問題3:ISN(Initial Sequence Number)是固定的嗎?

不固定,client_isn是隨機生成的,而server_isn則需要根據SYN報文中的源、IP和埠,加上伺服器本身密碼數進行相同散列得到,顯然也不固定。

問題4:第三次握手失敗了怎麼辦?

  1. 在第2次握手中,server向client發送SYN+ACK報文後,就會啟動一個定時器,等待client返回的ACK報文。
  2. 如果第三次失敗,client給server返回了ACK報文,server並不能收到這個ACK報文。那server就會啟動超時重傳機制,超過規定時間會重新發起第2次握手,向client發送SYNACK。重傳次數默認5次。
  3. 如果到重傳指定次數,仍未收到ACK應答,那一段時間後server會關閉這個連接。但client認為這個連接已建立,如果它向server寫數據,server將回應RST包、強制關閉TCP連接,以防止SYN攻擊。

問題5:什麼是SYN攻擊?如何防範?

在三次握手過程中,伺服器發送SYN-ACK之後,收到客戶端ACK之前的TCP連接稱為半連接(half-open connect)。此時伺服器處於SYN-RECV。當收到ACK後,伺服器轉入ESTABLISHED狀態。

SYN攻擊就是攻擊客戶端,在短時間內偽造大量不存在的IP地址,向伺服器不斷發送SYN包,伺服器回復確認包,並等待客戶確認。 由於源地址不存在,伺服器需要不斷重發直至超時,這些偽造SYN包將長時間佔用未連接隊列,正常SYN請求被丟棄,目標系統運行緩慢,嚴重者引起網路堵塞甚至系統癱瘓。

SYN攻擊是一種典型的DoSe/DDoS攻擊。

問題6:如何檢測SYN攻擊?

檢測SYN攻擊非常方便,當你在伺服器上看到大量半連接狀態時,特別是IP地址是隨機的,基本可以斷定這是一次SYN攻擊,在Linux/Unix可用netstat命令檢測。

問題7:如何防禦SYN攻擊?

SYN攻擊不能完全被阻止,除非重新設計TCP協議。能做的就是就是儘可能減輕SYN攻擊危害,常見防禦方法有:縮短超時(SYN Timeout)時間、增大最大半連接數、過濾網關防護、SYN cookies技術。

問題8:握手過程中除了序號的同步,還會同步什麼資訊?

  • 1)序號;
  • 2)標誌位:SYN(發起一個連接)、ACK(確認序號有效);
  • 3)窗口(流量控制中的接收窗口)。

問題9:通過什麼方式去知道某台電腦上還能建立多少個TCP連接?

系統用四元組{Local IP,Local Port,Remote IP,Remote Port} 來唯一標識TCP連接。

Client發起TCP連接時,系統通常會選取一個空閑本地埠(Local Port),該埠,類型是Unsigned short,最大65536,埠0有特殊含義,因此最大可用埠,即最大TCP連接數只有65535。

Server部分的Remote IP和Remote Port可變,因此最大TCP連接為客戶端IP數*客戶端Port數,IPV4簡單情況(不考慮地址分類) 最大連接數為232(IP數)* 216(Port數) ,也就是Server端單機最大TCP連接數約為248

3. 四次揮手(four-way handshake)

3.1 含義

TCP連接拆除需要發送四個包,因此稱為四次揮手。

Client或Server均可主動發起。在socket編程中,執行close()操作即可產生揮手操作。

3.2 四次揮手過程

四次揮手

  1. 客戶端A發送一個FIN,用來關閉客戶A到伺服器B的數據傳送。

  2. 伺服器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1。和SYN一樣,一個FIN將佔用一個序號。

  3. 伺服器B關閉與客戶端A的連接,發送一個FIN給客戶端A。

  4. 客戶端A發回ACK報文確認,並將確認序號設置為收到序號加1。

3.3 深入理解TCP連接的釋放

由於TCP連接是全雙工的,因此每個方向都必須單獨進行關閉。這原則是當一方完成它的數據發送任務後就能發送一個FIN來終止這個方向的連接。收到一個 FIN只意味著這一方向上沒有數據流動,一個TCP連接在收到一個FIN後仍能發送數據。首先進行關閉的一方將執行主動關閉,而另一方執行被動關閉。
TCP協議的連接是全雙工連接,一個TCP連接存在雙向的讀寫通道。
簡單說來是 「先關讀,後關寫」,一共需要四個階段。以客戶機發起關閉連接為例:

  1. 伺服器讀通道關閉
  2. 客戶機寫通道關閉
  3. 客戶機讀通道關閉
  4. 伺服器寫通道關閉

關閉行為是在發起方數據發送完畢之後,給對方發出一個FIN(finish)數據段。直到接收到對方發送的FIN,且對方收到了接收確認ACK之後,雙方的數據通訊完全結束,過程中每次接收都需要返回確認數據段ACK。

3.4 TCP狀態遷移

狀態遷移

客戶端TCP狀態遷移:

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

伺服器TCP狀態遷移:

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

各個狀態的意義如下:

LISTEN - 偵聽來自遠方TCP埠的連接請求; 
SYN-SENT - 在發送連接請求後等待匹配的連接請求; 
SYN-RECEIVED - 在收到和發送一個連接請求後等待對連接請求的確認; 
ESTABLISHED - 代表一個打開的連接,數據可以傳送給用戶; 
FIN-WAIT-1 - 等待遠程TCP的連接中斷請求,或先前的連接中斷請求的確認;
FIN-WAIT-2 - 從遠程TCP等待連接中斷請求; 
CLOSE-WAIT - 等待從本地用戶發來的連接中斷請求; 
CLOSING - 等待遠程TCP對連接中斷的確認; 
LAST-ACK - 等待原來發向遠程TCP的連接中斷請求的確認; 
TIME-WAIT - 等待足夠的時間以確保遠程TCP接收到連接中斷請求的確認; 
CLOSED - 沒有任何連接狀態;

SYN_RECV

服務端收到建立連接的SYN沒有收到ACK包的時候處在SYN_RECV狀態。處在SYNC_RECV的TCP連接稱為半連接,並存儲在內核的半連接隊列中,在內核收到對端發送的ack包時會查找半連接隊列,並將符合的requst_sock資訊存儲到完成三次握手的連接的隊列中,然後刪除此半連接 。

CLOSE_WAIT

發起TCP連接關閉的一方稱為client,被動關閉的一方稱為server。在已經接收到FIN,但是還沒有發送自己的FIN的時刻,連接處於CLOSE_WAIT狀態。出現這種狀況一般都是由於server端程式碼的問題,如果伺服器出現大CLOSE_WAIT,應該要考慮檢查程式碼 。

TIME_WAIT

根據TCP協議定義的3次握手斷開連接規定,發起socket主動關閉的一方 socket將進入TIME_WAIT狀態。TIME_WAIT狀態將持續2個MSL(Max Segment Lifetime),在Windows下默認為4分鐘,即240秒。TIME_WAIT狀態下的socket不能被回收使用。

問題1:為什麼建立連接是三次握手,而關閉連接卻是四次揮手呢?

關鍵在中間兩步:

  • 這是因為服務端在LISTEN狀態下,收到建立連接請求的SYN報文後,把ACK和SYN放在一個報文里發送給客戶端。
  • 而關閉連接時,收到對方的FIN報文時,僅表示對方不再發送,但還能接收數據,己方也未必全部發送完畢。所以只能先回復一個ACK報文,告訴客戶端「你發的FIN報文已收到」。等伺服器所有報文發送/接收完,才能發送FIN報文。因此ACK和SYN分開發送,要四次握手。

問題2:四次揮手釋放連接時,等待2MSL的意義?

  1. 保證客戶端發送的最後1個ACK報文能到達伺服器。假設網路不可靠,ACK報文丟失。如果服務端發出FIN報文後沒收到ACK報文,就會重發FIN報文(收到不再發),此時處於TIME-WAIT的客戶端就會重發ACK報文。
    當然,客戶端也不能無限等待這個FIN報文,需要設置一個定時器,2MSL正好,因為1個最大發送和1個回復最長時間沒收到FIN,可以推斷ACK報文已經被伺服器接收,所以結束TCP連接。
  2. 防止已失效的連接請求報文段出現在新連接中。客戶端發完最後1個ACK報文後,再經過2MSL時間,就可以使網路不通暢產生的滯留報文段失效,這樣下一個新連接中就不會出現舊的連接請求報文。

問題3:四次揮手過程中服務端的哪幾種狀態,哪幾種包?

  1. 收到FIN之前是ESTABLISHED狀態;
  2. 發完ACK過程變為CLOSE_WAIT(關閉等待) 狀態;
  3. 發完FIN報文後是LAST_ACK(最後確認) 狀態;
  4. 收到客戶端ACK報文後變為CLOSED狀態。