計算機網絡傳輸協議TCP三次握手與四次揮手原理

TCP三次握手四次揮手

我們之前在 傳輸層協議TCP與UDP 中詳細介紹瞭UDP協議和TCP協議格式以及他們各自的特點,我們知道TCP協議是面向連接的,那面向連接就得需要做建立連接,維護連接,斷開連接這些工作。

TCP三次握手四次揮手

服務器狀態轉換

[CLOSED->LISTEN] 服務器調用listen後就進入LISTEN狀態,等待客戶端向自己發起連接

[LISTEN->SYN_RCVD] 一旦監聽到連接請求(同步報文段),就會將該連接放入內核中維護的連接等待隊列中,並向客戶端發送SYN+ACK報文確認已收到看客戶端的連接請求

[SYN_RCVD->ESTABLISHED] 服務器一旦收到瞭客戶端的確認報文,就進入ESTABLISHED狀態,就可以進行讀寫數據瞭

[ESTABLISHED->CLOSE_WAIT] 當客戶端主動關閉連接(調用close),服務器收到客戶端發送的結束報文段FIN,服務器向客戶端返回確認收到關閉連接的報文後,就會進入CLOSE_WAIT

[CLOSE_WAIT->LAST_ACK] 當服務器進入CLOSE_WAIT狀態時,說明服務器已經開始準備關閉連接(但是需要先處理完之前的數據),當服務器真正調用close關閉連接時,會向客戶端發送FIN報文,自己則進入LAST_ACK狀態,等待最後一個ACK的到來(這裡的ACK是指客戶端對於服務器發送的FIN的響應報文)

[LAST_ACK->CLOSED] 服務器收到瞭客戶端對FIN報文的響應ACK,至此服務器關閉連接成功

客戶端狀態轉換

[CLOSED->SYN_SEND] 客戶端調用connect,向服務器發送同步報文段,表示想與服務器建立連接,自己進入SYN_SEND狀態等待服務器的響應

[SYN_SEND->ESTABLISHED] connect調用成功,客戶端收到服務器的響應報文ACK,進入ESTABLISHED狀態,可以讀寫數據

[ESTABLISHED->FIN_WIAT_1] 客戶端主動調用close,向服務器發送結束報文段,自己進入FIN_WAIT_1狀態,等待服務器的響應

[FIN_WAIT_1->FIN_WAIT_2]客戶端收到服務器對結束報文段的確認,則進入FIN_WAIT_2狀態,開始等待服務器的結束報文段

[FIN_WAIT_2 -> TIME_WAIT] 客戶端收到服務器發來的結束報文段, 進入TIME_WAIT, 並發出對服務器發送的結束報文的的響應 LAST_ACK;

[TIME_WAIT -> CLOSED] 客戶端要等待一個2MSL(Max Segment Life, 報文最大生存時間)的時間, 才會進入CLOSED狀態(因為要防止服務器沒有收到LASK_ACK,需要進行重發的情況).

TCP狀態轉換圖

TCP狀態轉換圖

TCP中常見的面試題

為什麼是三次握手,不是一次或者兩次

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

  • 現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作為例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到瞭這個分組,並發送瞭確認應答分組。
    按照兩次握手的協定,S認為連接已經成功地建立瞭,可以開始發送數據分組。
    可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已準備好,不知道S建立什麼樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。
    在這種情況下,C認為連接還未建立成功,將忽略S發來的任何數據分 組,隻等待連接確認應答分組。而S在發出的分組超時後,重復發送同樣的分組。這樣就形成瞭死鎖。
  • 兩次握手還可能造成服務器資源的浪費,怎麼說呢,再舉個栗子,假設今天C向S發送瞭一個連接請求,並等待S的響應,S給C發送ACK之後,就認為連接已經建立,我們知道連接建立之後是需要維護這個連接的,此時S的操作系統就需要分配資源和空間來維護這個連接,那假設S給C的響應丟失瞭,C並未收到響應則認為連接沒有建立成功,不能正常通信,此時S維護的連接就是一個失敗的連接,不能成功通信,那假設今天由100萬個客戶端向S這個服務器發送連接請求,結果都沒有收到響應,此時S就維護瞭100萬個無用的連接,浪費服務器的資源

為什麼是三次握手,四次揮手

答:因為當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。

但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以隻能先回復一個ACK報文,告訴Client端,“你發的FIN報文我收到瞭”。

隻有等到Server端所有的報文都發送完瞭,所有的數據也處理完瞭之後,Server端才能發送FIN報文表示自己能夠斷開連接,因此不能一起發送。故需要四步握手。

如果已經建立瞭連接,但是客戶端突然出現故障瞭怎麼辦?

答:TCP連接是有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。

服務器每收到一次客戶端的請求後都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以後每隔75秒鐘發送一次。

若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出瞭故障,接著就關閉連接。

為什麼會有TIME_WAIT狀態

現在做一個測試,首先啟動server,然後啟動client,然後用Ctrl-C使server終止,這時馬上再運行server, 結果是:

這是因為,雖然server的應用程序終止瞭,但TCP協議層的連接並沒有完全斷開,因此不能再次監 聽同樣的server端口.我們用netstat命令查看一下:

理解TIME_WAIT

  • TCP協議規定,主動關閉連接的一方要處於TIME_ WAIT狀態,等待兩個MSL(maximum segment lifetime)的時間後才能回到CLOSED狀態.
  • 我們使用Ctrl-C終止瞭server, 所以server是主動關閉連接的一方, 在TIME_WAIT期間仍然不能再次監聽同樣的server端口;
  • MSL在RFC1122中規定為兩分鐘,但是各操作系統的實現不同, 在Centos7上默認配置的值是60s;
  • 可以通過 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看msl的值

我們來想一想,為什麼TIME_WAIT的時間是2MSL

MSL是TCP報文的最大生存時間, 因此TIME_WAIT持續存在2MSL的話就能保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失(否則服務器立刻重啟, 可能會收到來自上一個進程的遲到的數據, 但是這種數據很可能是錯誤的);

同時也是在理論上保證最後一個報文可靠到達(假設最後一個ACK丟失, 那麼服務器會再重發一個FIN. 這時雖然客戶端的進程不在瞭, 但是TCP連接還在, 仍然可以重發LAST_ACK);

解決TIME_WAIT狀態引起的bind失敗的方法

  • 在server的TCP連接沒有完全斷開之前不允許重新監聽, 某些情況下可能是不合理的
  • 服務器需要處理非常大量的客戶端的連接(每個連接的生存時間可能很短, 但是每秒都有很大數量的客戶端來請求).
  • 這個時候如果由服務器端主動關閉連接(比如某些客戶端不活躍, 就需要被服務器端主動清理掉), 就會產生大量TIME_WAIT連接.
  • 由於我們的請求量很大, 就可能導致TIME_WAIT的連接數很多, 每個連接都會占用一個通信五元組(源ip,源端口, 目的ip, 目的端口, 協議). 其中服務器的ip和端口和協議是固定的. 如果新來的客戶端連接的ip和端口號和TIME_WAIT占用的鏈接重復瞭,就會出現問題.
  • 使用setsockopt() 設置socket描述符的 選項SO_REUSEADDR為1, 表示允許創建端口號相同但IP地址不同的多個socket描述符

setsockopt

  • 加上瞭setsockopt之後,ctrl+c終止服務器之後也可以馬上啟動服務器

setsocket

以上就是計算機網絡傳輸協議TCP三次握手與四次揮手原理的詳細內容,更多關於TCP三次握手與四次揮手的資料請關註WalkonNet其它相關文章!

推薦閱讀: