天天看點

tcp的四次揮手(為什麼三次握手和四次揮手)

大家好,又見面了,我是你們的朋友全棧君。

TCP四次揮手過程和狀态變遷

tcp的四次揮手(為什麼三次握手和四次揮手)

在斷開連接配接之前用戶端和伺服器都處于ESTABLISHED狀态,雙方都可以主動斷開連接配接,以用戶端主動斷開連接配接為優。

第一次揮手:用戶端打算斷開連接配接,向伺服器發送FIN封包(FIN标記位被設定為1,1表示為FIN,0表示不是),FIN封包中會指定一個序列号,之後用戶端進入FIN_WAIT_1狀态。

也就是用戶端發出連接配接釋放封包段(FIN封包),指定序列号seq = u,主動關閉TCP連接配接,等待伺服器的确認。

第二次揮手:伺服器收到連接配接釋放封包段(FIN封包)後,就向用戶端發送ACK應答封包,以用戶端的FIN封包的序列号 seq+1 作為ACK應答封包段的确認序列号ack = seq+1 = u + 1。

接着伺服器進入CLOSE_WAIT(等待關閉)狀态,此時的TCP處于半關閉狀态(下面會說什麼是半關閉狀态),用戶端到伺服器的連接配接釋放。用戶端收到來自伺服器的ACK應答封包段後,進入FIN_WAIT_2狀态。

第三次握手:伺服器也打算斷開連接配接,向用戶端發送連接配接釋放(FIN)封包段,之後伺服器進入LASK_ACK(最後确認)狀态,等待用戶端的确認。

伺服器的連接配接釋放(FIN)封包段的FIN=1,ACK=1,序列号seq=m,确認序列号ack=u+1。

第四次握手:用戶端收到來自伺服器的連接配接釋放(FIN)封包段後,會向伺服器發送一個ACK應答封包段,以連接配接釋放(FIN)封包段的确認序号 ack 作為ACK應答封包段的序列号 seq,以連接配接釋放(FIN)封包段的序列号 seq+1作為确認序号ack。

之後用戶端進入TIME_WAIT(時間等待)狀态,伺服器收到ACK應答封包段後,伺服器就進入CLOSE(關閉)狀态,到此伺服器的連接配接已經完成關閉。

用戶端處于TIME_WAIT狀态時,此時的TCP還未釋放掉,需要等待2MSL後,用戶端才進入CLOSE狀态。

由用戶端到伺服器需要一個FIN和ACK,再由伺服器到用戶端需要一個FIN和ACK,是以通常被稱為四次握手。

用戶端和伺服器都可以主動關閉連接配接,隻有率先請求關閉的一方才會進入TIME_WAIT(時間等待狀态)。

為什麼揮手需要四次?

這是由于TCP的半關閉(half-close)造成的。半關閉是指:TCP提供了連接配接的一方在結束它的發送後還能接受來自另一端資料的能力。通俗來說,就是不能發送資料,但是還可以接受資料。

TCP不允許連接配接處于半打開狀态時,就單向傳輸資料,是以完成三次握手後才可以傳輸資料(第三握手可以攜帶資料)。

當連接配接處于半關閉狀态時,TCP是允許單向傳輸資料的,也就是說伺服器此時仍然可以向用戶端發送資料,等伺服器不再發送資料時,才會發送FIN封包段,同意現在關閉連接配接。

這一特性是由于TCP雙向通道互相獨立所導緻的,也使得關閉連接配接必須經過四次握手。

可能有些人會有疑惑:為什麼中間的ACK和FIN不可以像三次握手那樣合為一個封包段呢?

在socket網絡程式設計中,執行close()方法會觸發核心發送FIN封包。什麼時候調用close()方法,這是由使用者态決定的,假如伺服器仍有大量資料等待處理,那麼伺服器會等資料處理完後,才調用close()方法,這個時間可能會很久,而ACK封包則是由系統核心來完成的,這個過程會很快。是以中間的ACK和FIN不能合為一個包。

為什麼TIME_WAIT等待的時間是2MSL?

MSL(Maximum Segment LifeTime)是封包最大生成時間,它是任何封包在網絡上存在的最長時間,超過這個時間的封包将被丢棄。

因為TCP協定是基于IP協定(位于IP協定的上一層),IP資料報中有限制其生存時間的TTL字段,是IP資料報可以經過的最大路由器的個數,每經過處理它的路由,TTL就會減一。TTL為 0 時還沒有到達目的地的資料報将會被丢棄,同時發送 ICMP 封包通知源主機。

MSL的機關為時間,TTL的機關為跳轉數。是以MSL應該大于等于TTL變為0的時間,以確定封包已被丢棄。

TIME_WAIT等待的2MSL時間,可以了解為資料報一來一回所需要的最大時間。

2MSL時間是從用戶端接收到FIN後發送ACK開始計時的。如果在這個時間段内,伺服器沒有收到ACK應答封包段,會重發FIN封包段,如果用戶端收到了FIN封包段,那麼2MSL的時間将會被重置。如果在2MSL時間段内,沒有收到任何資料報,用戶端則會進入CLOSE狀态。

等待2MSL的意義

1.保證用戶端最後發送的ACK能夠到達伺服器,幫助其正常關閉。

由于這個ACK封包段可能會丢失,使得處于LAST_ACK狀态的伺服器得不到對已發送FIN封包段的确認,進而會觸發逾時重傳。伺服器會重發FIN封包段,用戶端能保證在2MSL時間内收到來自伺服器的重傳FIN封包段,進而用戶端重新發送ACK應答封包段,并重置2MSL計數。

假如用戶端不等待2MSL就之間進入CLOSE狀态,那麼伺服器會一直處于LAST_ACK狀态。

當用戶端發起建立SYN封包段請求建立新的連接配接時,服務端會發送RST封包段給用戶端,連接配接建立的過程就會被終止。

2.防止已失效的連接配接請求封包段出現在本連接配接中。

TIME_WAIT等待的2MSL時間,確定本連接配接内所産生的所有封包段都從網絡中消失,使下一個新的連接配接中不會出現這種舊的連接配接請求封包段。

TIME_WAIT狀态過多有什麼危害?

隻有主動發起斷開請求的一方才會進入TIME_WAIT狀态!

1.占用系統資源

2.socket的TIME_WAIT狀态結束之前,該socket占用的端口号将一直無法釋放。如果伺服器TIME_WAIT狀态過多,占滿了所有端口資源,則會導緻無法建立新的連接配接。

如何解決TIME_WAIT狀态過多?

最好的辦法是盡量讓用戶端主動斷開連接配接,除非遇到一些異常情況,如用戶端協定錯誤、用戶端逾時等。

打開系統的TIME_WAIT重用和快速回收。

在Linux系統可以修改以下參數:

1.打開TCP對時間戳的支援,保持伺服器與用戶端時間同步

net.ipv4.tcp_timestamps=1(預設即為 1)           

複制

2.修改net.ipv4.tcp_tw_reuse = 1,允許對處于TIME_WAIT的socket用于建立新的連接配接

net.ipv4.tcp_tw_reuse = 1 (預設為0)           

複制

修改TIME_WAIT連接配接狀态的上限值,超過上限值,處于TIME_WAIT狀态的socket将立刻被清除并列印警告資訊。

net.ipv4.tcp_max_tw_buckets = 18000,表示系統同時保持處于TIME_WAIT狀态的socket的最大數量,預設為18000。

可修改為更小值。

net.ipv4.tcp_max_tw_buckets = 6666           

複制

還有一個是修改短連接配接為長連接配接的方式(目前沒有學到)。

釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/129295.html原文連結:https://javaforall.cn