TCP為什麼是三次握手,而不是兩次或四次?

三旺通信


所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。在socket編程中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:

TCP是傳輸控制協議。

  • syn是該協議中的一個標誌位。如果該位被置為1,則表示這個報文是一個請求建立連接的報文。
  • ack也是該協議的一個標誌位。如果該位被置為1,則表示這個報文是一個用於確認的報文。

seq是序列號,這是為了連接以後傳送數據用的,ack是對收到的數據包的確認,值是等待接收的數據包的序列號。在第一次消息發送中,A隨機選取一個序列號x作為自己的初始序號發送給B;第二次消息B使用ack對A的數據包進行確認,因為已經收到了序列號為x的數據包,準備接收A序列號為x+1的包,所以ack=x+1,同時B告訴A自己的初始序列號,就是seq=y;第三條消息A告訴B收到了B的確認消息並準備建立連接,A自己此條消息的序列號是x+1,所以seq=x+1,而ack=y+1是表示A正準備接收B序列號為y+1的數據包。seq是數據包本身的序列號;ack是期望對方繼續發送的那個數據包的序列號。

(1)第一次握手:Client將標誌位SYN((同步序列編號(Synchronize Sequence Numbers))置為1,隨機產生一個值seq=J,並將該數據包發送給Server,Client進入SYN_SENT狀態,等待Server確認。

(2)第二次握手:Server收到數據包後由標誌位SYN=1知道Client請求建立連接,Server將標誌位SYN和ACK都置為1,ack=J+1,隨機產生一個值seq=K,並將該數據包發送給Client以確認連接請求,Server進入SYN_RCVD狀態。

(3)第三次握手:Client收到確認後,檢查ack是否為J+1,ACK是否為1,如果正確則將標誌位ACK置為1,ack=K+1,並將該數據包發送給Server,Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立成功,Client和Server進入ESTABLISHED狀態,完成三次握手,隨後Client與Server之間可以開始傳輸數據了。 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攻擊了,使用如下命令可以讓之現行:#netstat -nap | grep SYN_RECV


石墨烯大傅


你聽到了嗎?

聽到了,你呢?

我也聽到了

三次之後確保雙方能通信,少哪句都是不通的


O0o0o0o0O


TCP的三次握手,基本算上是BAT面試過程的高頻考點了。基本是程序員面試必備知識點了,無論你是前端還是後臺開發,下面是一張簡化版的TCP狀態圖,如果需要高清版,可關注私信或留言即可。


下面言歸正傳,對題主的問題進行回答。首先我們需要明白到底什麼是三次握手!

什麼是三次握手?

下面的過程來自維基百科的解釋:

  1. 客戶端通過向服務器端發送一個SYN來創建一個主動打開,作為三次握手的一部分。客戶端把這段連接的序號設定為隨機數A。

  2. 服務器端應當為一個合法的SYN回送一個SYN/ACK。ACK的確認碼應為A+1,SYN/ACK包本身又有一個隨機產生的序號B。

  3. 最後,客戶端再發送一個ACK。此時包的序號被設定為A+1,而ACK的確認碼則為B+1。當服務端收到這個ACK的時就完成了三次握手,並進入了連接創建狀態。


為什麼是三次,而不是二次或者四次?

還記得我們初中數學常用的反證法嗎?在這裡同樣適用。

0x01. 假設TCP連接過程採用「四次握手」,我們來模擬下其過程:

  1. A 發送同步信號SYN + A'sInitial sequence number

  2. B 確認收到A的同步信號,並記錄A's ISN 到本地,命名 B's ACK sequence number

  3. B發送同步信號SYN + B's Initial sequence number

  4. A確認收到B的同步信號,並記錄B's ISN 到本地,命名 A's ACK sequence number


很顯然,步驟2和步驟3重複了,可以合併,只需三次握手即可完成,否則會造成網絡資源的浪費。


0x02. 假設TCP連接過程採用「二次握手」,模擬過程如下:

  1. A 發送同步信號SYN + A'sInitial sequence number

  2. B發送同步信號SYN + B'sInitial sequence number + B's ACK sequence number


看出問題了吧,A的初始序列號在A、B之間達成了一致,但是B無法知道A是否已經接收到自己的同步信號,如果這個同步信號丟失了,A和B就B的初始序列號將無法達成一致。


所以,TCP的設計者才選用了三次握手機制。


一個程序員的奮鬥史


一、三次握手

三次握手:在通信之前,會先通過三次握手的機制來確認兩端口之間的連接是否可用。而UDP不需要確認是否可用,直接傳。

三次握手機制

一開始客戶端和服務端都是關閉狀態,但是在某個時刻,客戶端需要和服務端進行通信,此時雙方都會各自準備好端口,服務器段的端口會處於監聽狀態,等待客戶端的連接。

客戶端可會知道自己的端口號,和目的進程的端口號,這樣才能發起請求。

第一次握手:客戶端想與服務器進行連接了,所以狀態變為主動打開,同時發送一個連接請求報文給服務器段SYN=1,並且會攜帶x個字節過去。

發送完請求連接報文後,客戶端的狀態就變為了SYN_SENT,可以說這個狀態是等待發送確認(為了發送第三次握手時的確認包)

第二次握手:服務端接收到連接請求報文後,從LSTTEN狀態變為被動打開狀態,然後給客戶端返回一個報文。這個報文有兩層意思,一是確認報文,而可以達到告訴客戶端,我也打開連接了。

發完後,變為SYN_RCVD狀態(也可以說是等待接受確認狀態,接受客戶端發過來的確認包)

第三次握手:客戶端得到服務器端的確認和知道服務器端也已經準備好了連接後,還會發一個確認報文到服務器端,告訴服務器端,我接到了你發送的報文,接下來就讓我們兩個進行連接了。

客戶端發送完確認報文後,進入ESTABLISHED,而服務器接到了,也變為ESTABLISHED

進入到ESTABLISHED狀態後,連接就已經完成了,可以進行通信了。

問題:為什麼需要第三次握手,有前面兩次不就已經可以了嗎?

假設沒有第三次握手,客戶端發送一個連接請求報文過去,但是因為網絡延遲,在等待了一個超時時間後,客戶端就會再重新發一個請求連接報文過去,然後正常的進行;

服務器端發回一個確認連接報文,然後就開始通訊,通訊結束後,那個第一次因為網絡延遲的請求連接報文到了服務器端,服務器端不知道這個報文已經失效,也發回了一個確認連接報文。

客戶端接收後,發現自己並沒有發送連接請求(因為超時了,所以就認為自己沒有發),所以對這個確認連接請求就什麼也不做,但是此時客戶端不這麼認為,他認為連接已經建立了,就一直打開著等待客戶端傳數據過來,這就造成了極大的浪費。

如果有了第三次握手,那麼客戶端就可以通知服務器了,所以第三次握手也很重要!

二、四次揮手

四次揮手是用來斷開服務器和客戶端之間的通信的,之所以要斷開連接,是因為TCP/IP 協議是要佔用端口號的,而計算機的端口卻是有限的,不進行斷開的話,勢必會造成計算機資源的浪費。

1、在整個通信的過程中,誰先發起請求,誰就是客戶端。

當客戶端的數據傳輸到尾部時,客戶端向服務器發送帶有FIN標誌的數據包,使其明白自己準備斷開通信了。

2、因為TCP的通信是使用全雙工通信的WebSocket,所以在斷開連接的時候也應該是雙向的;當服務器收到帶有FIN標誌的數據包時,其必不會直接發送FIN標誌斷開通信的請求,而是先發送一個帶有ACK標誌的應答信息,使客戶端明白服務器還有數據要進行發送。

3、當 服務器的數據發送完成後,向客戶端發送帶有FIN標誌的數據包,通知客戶端斷開連接。

4、這一次揮手是我覺得四次揮手中設計的最巧妙的一次。

當客戶端收到FIN後,擔心網絡上某些不可控制的因素導致服務器不知道他要斷開連接,會發送ACK進行確認,同時把自己設置成TIME_WAIT狀態並啟動定時器,**在TCP的定時器到達後客戶端並沒有接收到請求,會重新發送;當服務器收到請求後就斷開連接;當客戶端等待2MLS(兩倍報文最大生存時間)後,沒有收到請求重傳的請求後,客戶端這邊就斷開連接,**整個TCP通信就結束了。

四次揮手的圖如下所示:

問題:關閉的時候為什麼會是四次揮手?

四次揮手不能像三次握手一樣,三次握手可以將ACK+SYN 一起發送,ACK用於確認信息,SYN卻是用來建立聯機的;

四次揮手中ACK是不能和FIN一起發送,ACK只是告訴客戶端確認我收到了,等我將數據發送完畢之後會向其發送FIN的標誌,所以四次揮手是不能夠改變的。


三旺通信


網上有很多文章講TCP為什麼建立連接時需要三次握手,關閉連接時需要四次握手,講了很多原理,反而讓很多人難以理解。

其實只有一句話:TCP連接是兩個端點之間的事,由於TCP連接是可靠連接,所以不管是建立連接還是關閉連接,需要兩個端點都要發送請求和收到確認

其次要理解TCP的通道是全雙工的,是可以讀和寫數據的,理解這個之後就明白了為什麼關閉連接時需要四次握手。

首先講三次握手,建立連接時不涉及到讀寫通道,只是兩個端點的請求和確認。要記住是兩個端點之間的,兩個端點是平等的關係。

從客戶端的角度來說,它只需要發送一個請求syn,然後收到ack就能知道連接是可以建立的

從服務器端的角度,它也只需要發送一個請求syn,然後收到ack就能知道連接是可以建立的

所以建立連接時最少只需要三次握手即可,即服務器端收到客戶端syn,之後向客戶端發送ack的同時攜帶上自己的syn,這樣就只需要三次握手就能使雙方都發生syn和都收到ack.

當然你自己設計的時候也可以使用4次或者更多的握手,但是最少3次即可

關閉連接時也還是兩個端點都要發送請求和收到確認。但是關閉連接時涉及到兩個端點的讀寫通道的事。

從客戶端的角度來說,它發送一個關閉請求FIn,然後收到ack就知道可以關閉連接的

從服務器的角度來說,它發送一個關閉請求Fin,然後收到ack就知道可以關閉連接的

但是由於是讀和寫通道,所以服務器端在收到客戶端的關閉Fin時,可能還在往客戶端發送消息,所以只能先發送ack確認客戶端客戶關閉寫通道,這時候服務端不能使用通過一消息發送它的關閉Fin,只能過會等它自己寫完了,才能重新發送一個關閉請求的Fin。

所以比建立連接時多了一次握手,關閉連接時最少需要4次握手。理解了為什麼需要4次握手之後,就不難理解TCP半關閉的概念。


程序猿之奇異世界


分析

一個虛擬連接的建立是通過三次握手來實現的

1.(Client)[SYN](Server)

假如Client和Server通訊. 當Client要和Server通信時,Client首先向Server發一個SYN (Synchronize) 標記的包,告訴Server請求建立連接.

注意: 一個 SYN包就是僅SYN標記設為1的TCP包(參見TCP包頭Resources). 認識到這點很重要,只有當Server收到Client發來的SYN包,才可建立連接,除此之外別無他法。因此,如果你的防火牆丟棄所有的發往外網接口的SYN包,那麼你將不 能讓外部任何主機主動建立連接。

2. (Client) [SYN/ACK] (Server)

接著,Server收到來自Client發來的SYN包後,會發一個對SYN包的確認包(SYN/ACK)給Client,表示對第一個SYN包的確認,並繼續握手操作.

注意: SYN/ACK包是僅SYN 和 ACK 標記為1的包.

3.(Client) [ACK] (Server)

Client收到來自Server的SYN/ACK 包,Client會再向Server發一個確認包(ACK),通知Server連接已建立。至此,三次握手完成,一個TCP連接完成。

Note: ACK包就是僅ACK 標記設為1的TCP包. 需要注意的是當三此握手完成、連接建立以後,TCP連接的每個包都會設置ACK位。

四次揮手用來關閉已建立的TCP連接

· (Client) ACK/FIN (Server)

· (Client) ACK (Server)

· (Client) ACK/FIN (Server)

· (Client) ACK (Server)


小福的故事


為了實現可靠數據傳輸, TCP 協議的通信雙方, 都必須維護一個序列號, 以標識發送出去的數據包中, 哪些是已經被對方收到的。 三次握手的過程即是通信雙方相互告知序列號起始值, 並確認對方已經收到了序列號起始值的必經步驟。

如果只是兩次握手, 至多隻有連接發起方的起始序列號能被確認, 另一方選擇的序列號則得不到確認

以下圖1是正確握手,圖2、3是如果是2次示例





子闕創客


第一次握手:客戶端發送TCP包,置SYN標誌位為1,將初始序號X,保存在包頭的序列號(Seq)裡。

第二次握手:服務端回應確認包,置SYN標誌位為1,置ACK為X+1,將初始序列號Y,保存在包頭的序列號裡。

第三次握手:客戶端對服務端的確認包進行確認,置SYN標誌位為0,置ACK為Y+1,置序列號為Z。

為什麼不是兩次

重新看一遍圖(S-服務端,C-客戶端)

第一次握手後,S可以確認自己收報文與C發報文的功能都正常,而C呢,它什麼都不能確認。

第二次握手後,C可以確認自己的收發報文與S的收發報文功能都正常,也就是認為連接已建立。

那麼第三次呢,S也可以確認雙方能夠正常通信。

假想一下,如果我們去掉了第三次呢?

如果只是第二次建立的話,服務端和客戶端就已經建立,但是如果客戶端沒有收到服務端的回應?這個時候,客戶端認為沒有建立,服務端卻為認為建立成功,並保存了必要的資源,如果出現大量的這樣的情況。那麼服務器會奔潰。

因此第三次握手是必要的。

為什麼不是四次

因為三次握手後,C和S至少可以確認之前的通信情況,但無法確認之後的情況。 所以如果四次還是五次或是更多次都是徒勞的



分享到:


相關文章: