从书上截取一段TCP三次握手和四次挥手
一、三次握手
(1)第一次握手:Client進入SYN_SENT狀態,發送一個SYN幀來主動打開傳輸通道,該幀的SYN標志位被設置為1,同時會帶上Client分配好的SN序列號,該SN是根據時間產生的一個隨機值,通常情況下每間隔4ms(毫秒)會加1。除此之外,SYN幀還會帶一個MSS(最大報文段長度)可選項的值,表示客戶端發送出去的最大數據塊的長度。
(2)第二次握手:Server在收到SYN幀之后,會進入SYN_RCVD狀態,同時返回SYN+ACK幀給Client,主要目的在于通知Client“Server已經收到SYN消息,現在需要進行確認”。Server發出的SYN+ACK幀的ACK標志位被設置為1,其確認序號AN(Acknowledgment Number)值被設置為Client的SN+1;SYN+ACK幀的SYN標志位被設置為1,SN值為Server生成的SN序號;SYN+ACK幀的MSS(最大報文段長度)表示的是Server的最大數據塊長度。
(3)第三次握手:Client在收到Server的第二次握手SYN+ACK確認幀之后,首先將自己的狀態從SYN_SENT變成ESTABLISHED,表示自己方向的連接通道已經建立成功,Client可以發送數據給Server了。然后,Client發ACK幀給Server,該ACK幀的ACK標志位被設置為1,其確認序號AN(Acknowledgment Number)值被設置為Server的SN+1。還有一種情況,Client可能會將ACK幀和第一幀要發送的數據合并到一起發送給Server。Server在收到Client的ACK幀之后會從SYN_RCVD狀態進入ESTABLISHED狀態。至此,Server方向的通道連接建立成功。Server可以發送數據給Client,TCP的全雙工連接建立完成。三次握手的交互過程如圖
?Client和Server完成了三次握手后,雙方就進入數據傳輸階段。數據傳輸完成后,連接將斷開。連接斷開的過程需要經歷四次揮手。
二、四次揮手
在TCP連接開始斷開(或者拆接)的過程中,連接的每個端都能獨立、主動地發起。四次揮手的具體過程如下:
(1)第一次揮手:主動斷開方(可以是客戶端,也可以是服務端),向對方發送一個FIN結束請求報文,此報文的FIN位被設置為1,并且正確設置Sequence Number(序列號)和AcknowledgmentNumber(確認號)。發送完成后,主動斷開方進入FIN_WAIT_1狀態,表示主動斷開方沒有業務數據要發送給對方,準備關閉Socket連接了。
(2)第二次揮手:正常情況下,在收到了主動斷開方發送的FIN斷開請求報文后,被動斷開方會發送一個ACK響應報文,報文的Acknowledgment Number(確認號)值為斷開請求報文的SequenceNumber(序列號)加1,該ACK確認報文的含義是:“我同意你的連接斷開請求”。之后,被動斷開方就進入了CLOSE-WAIT(關閉等待)狀態,TCP服務會通知高層的應用進程,對方向本地方向的連接已經關閉,已經沒有數據要發送了,若本地還要發送數據給對方,對方依然會接收。被動斷開方的CLOSE-WAIT(關閉等待)還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。主動斷開方在收到了ACK報文后,由FIN_WAIT_1轉換成FIN_WAIT_2狀態。
(3)第三次揮手:在發送完成ACK報文后,被動斷開方還可以繼續完成業務數據的發送,待剩余數據發送完成或者CLOSE-WAIT(關閉等待)截止后,被動斷開方會向主動斷開方發送一個FIN+ACK結束響應報文,表示被動斷開方的數據都發送完了,然后被動斷開方進入LAST_ACK狀態。
(4)第四次揮手:主動斷開方收到FIN+ACK斷開響應報文后,還需要進行最后的確認,向被動斷開方發送一個ACK確認報文,然后自己就進入TIME_WAIT狀態,等待超時后最終關閉連接。處于TIME_WAIT狀態的主動斷開方在等待完成2MSL的時間后,如果期間沒有收到其他報文,則證明對方已正常關閉,主動斷開方的連接最終關閉。被動斷開方在收到主動斷開方的最后ACK報文以后,最終關閉連接,什么也不用管了。四次揮手的全部交互過程如圖
處于TIME_WAIT狀態的主動斷開方在等待2MSL時間后才真正關閉連接通道。2MSL翻譯過來就是兩倍的MSL。MSL(Maximum Segment Lifetime)指的是一個TCP報文片段在網絡中的最大存活時間,2MSL就是一次消息來回(一個發送和一個回復)所需的最大時間。如果直到2MSL主動斷開方都沒有再一次收到對方的報文(如FIN報文),則可以推斷ACK已經被對方成功接收。此時,主動斷開方將最終結束自己的TCP連接。所以,TCP的TIME_WAIT狀態也稱為2MSL等待狀態。
有關MSL的具體時間長度,在RFC1122協議中推薦為2分鐘,在SICS(瑞典計算機科學院)開發的一個小型開源的TCP/IP協議棧——LwIP開源協議棧中默認為1分鐘,在源自Berkeley的TCP協議棧實現中默認為30秒。總體來說,TIME_WAIT(2MSL)等待狀態的時間長度一般維持在1~4分鐘。通過三次握手建立連接和四次揮手拆除連接,一次TCP的連接建立及拆除至少進行7次通信,可見其成本是很高的。
三、常見面試題
問題(1):為什么關閉連接時需要四次揮手,而建立連接卻只要三次握手?
關閉連接時,被動斷開方在收到對方的FIN結束請求報文時很可能沒有發送完業務數據,并不能立即關閉連接,被動方只能先回復一個ACK響應報文,告訴主動斷開方:“你發的FIN報文我收到了,只有等到我所有的業務報文都發送完了,我才能真正結束,在結束之前,我會發給你FIN+ACK報文的,你先等著”。所以,被動斷開方的確認報文需要拆成兩步,故總共需要四次揮手。在建立連接場景中,Server的應答可以稍微簡單一些。當Server收到Client的SYN連接請求報文后,其中ACK報文表示對請求報文的應答,SYN報文表示服務端的連接也已經同步開啟了,而ACK報文和SYN報文之間不會有其他報文需要發送,故而可以合二為一,可以直接發送一個SYN+ACK報文。所以,在建立連接時,只需要三次握手即可。
問題(2):為什么連接建立的時候是三次握手,可以改成兩次握手嗎?
三次握手完成兩個重要的功能:一是雙方都做好發送數據的準備工作,而且雙方都知道對方已準備好;二是雙方完成初始SN序列號的協商,雙方的SN序列號在握手過程中被發送和確認。如果把三次握手改成兩次握手,可能發生死鎖。兩次握手的話,缺失了Client的二次確認ACK幀,假想的TCP建立連接時的二次揮手可以如圖所示。
?在假想的TCP建立連接時的二次握手過程中,Client給Server發送一個SYN請求幀,Server收到后發送確認應答SYN+ACK幀。按照兩次握手的協定,Server認為連接已經成功地建立,可以開始發送數據幀。在這個過程中,如果確認應答SYN+ACK幀在傳輸中被丟失,Client沒有收到,Client將不知道Server是否已準備好,也不知道Server的SN序列號,Client認為連接還未建立成功,將忽略Server發來的任何數據分組,會一直等待Server的SYN+ACK確認應答幀。Server在發出的數據幀后,一直沒有收到對應的ACK確認后就會產生超時,重復發送同樣的數據幀。這樣就形成了死鎖。
問題(3):為什么主動斷開方在TIME-WAIT狀態必須等待2MSL?
原因之一:主動斷開方等待2MSL的時間是為了確保兩端都能最終關閉。假設網絡是不可靠的,被動斷開方發送FIN+ACK報文后,其主動方的ACK響應報文有可能丟失,這時的被動斷開方處于LAST-ACK狀態,由于收不到ACK確認被動方一直不能正常地進入CLOSED狀態。在這種場景下,被動斷開方會超時重傳FIN+ACK斷開響應報文,如果主動斷開方在2MSL時間內收到這個重傳的FIN+ACK報文,就會重傳一次ACK報文,然后再一次重新啟動2MSL計時等待,這樣就能確保被動斷開方能收到ACK報文,從而能確保被動方順利進入CLOSED狀態。只有這樣,雙方才都能夠確保關閉。反過來說,如果主動斷開方在發送完ACK響應報文后不是進入TIME_WAIT狀態去等待2MSL時間,而是立即釋放連接,則將無法收到被動方重傳的FIN+ACK報文,所以不會再發送一次ACK確認報文,此時處于LAST-ACK狀態的被動斷開方無法正常進入CLOSED狀態。
原因之二:防止“舊連接已失效的數據報文”出現在新連接中。主動斷開方在發送完最后一個ACK報文后再經過2MSL才能最終關閉和釋放端口。這就意味著,相同端口的新TCP新連接需要在2MSL的時間之后才能夠正常建立。2MSL這段時間內,舊連接所產生的所有數據報文都已經從網絡中消失了,從而確保下一個新的連接中不會出現這種舊連接請求報文。
問題(4):如果已經建立了連接,但是Client端突然出現故障了怎么辦?
TCP還設有一個保活計時器,Client如果出現故障,Server不能一直等下去,這樣會浪費系統資源。每收到一次Client的數據幀后,Server的保活計時器都會復位。計時器的超時時間通常設置為2小時,若2小時還沒有收到Client的任何數據幀,Server就會發送一個探測報文段,以后每隔75秒發送一次。若一連發送10個探測報文仍然沒有反應,Server就認為Client出了故障,接著關閉連接。如果覺得保活計時器的兩個多小時的間隔太長,可以自行調整TCP連接的保活參數。
總結
以上是生活随笔為你收集整理的从书上截取一段TCP三次握手和四次挥手的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Leetcode-第 73 场双周赛
- 下一篇: 追了源码,做了测试,终于实现python