爲什麼說臭名昭著呢?是因爲三次握手和四次揮手幾乎是面試必問的問題。爲了能更好的記憶,我儘量以最簡潔的語言陳述。
目錄
聲明:以下圖片來源於網絡
TCP報文格式
在介紹三次握手和四次揮手之前,先來了解一下TCP的報文信息,如下圖所示:
在這裏我們着重介紹一下6個控制位:URG、ACK、PSH、RST、SYN、FIN,這6個控制字段各佔1位。各個控制位的功能如下:
- URG:緊急指針標識,爲1時標識緊急指針有效。
- ACK:確認序號標識,爲1時確認序號有效,爲0時表示報文中不含確認信息。TCP規定,ACK爲1的時候有效,也規定連接建立後所有發送的報文的ACK位爲1.
- PUSH:爲1的時候,指示接收方應該在接收到此報文以後,儘快將報文交給應用程序,而不是存放於緩存區內。
- RST:用於重置由於主機崩潰或者其他原因而出現的錯誤連接。
- SYN:在建立連接的時候用於同步序列號。當SYN=1而ACK=0表示這是一個連接請求報文,若同意連接,則應在響應報文中使SYN=1,ACK=1。
- FIN:用於釋放TCP連接。爲1時表示發送方已經沒有數據發送了,即關閉本方的數據流。
瞭解了這些之後,我們就可以進入重頭戲,看看三次握手和四次揮手了。
三次握手
首先,三次握手在客戶端請求連接的時候進行,即執行到代碼connect()的時候開始進行,整個過程如下圖所示:
第一次握手:
首先,客戶端將報文SYN置爲1,然後隨機生成一個序列號x發送給服務端。
第二次握手:
服務端收到客戶端的報文之後,由於SYN=1,ACK=0(控制位默認爲0),認爲這是一個連接請求的報文。
同意連接,因此在將要發送的報文中另SYN=1,ACK=1,將確認號置爲x+1,並且隨機生成一個序列號y。
確認號和序列號在TCP報文格式中都有展示。
第三次握手:
客戶端進行連接確認,在發送報文中令ACK=1(這時不是請求連接,因此不用設置SYN),序列號設置爲x+1,確認號設置爲y+1
三次握手之後TCP連接建立。
爲什麼是三次?
在這裏解釋一下,爲什麼是三次握手?這也是一個出現率比較高的面試題。
要回答問題,要從每次握手的意義(作用上)進行分析:
首先TCP是面向連接的,一次握手肯定不能建立連接,一條信息發出去,對方都沒有回肯定建立不了連接。
同時兩次握手會產生連接誤判的情況。多餘三次握手又會過於冗餘。
第一次握手:
客戶端什麼都不能確認,服務端可以確認對方發送正常。
第二次握手:
客戶端確認了自己發送接收正常,對方發送接收正常。
服務器確認自己接收正常,對方發送正常。
第三次握手:
客戶端確認自己和對方發送接收正常。
服務器確認自己和對方發送接收正常。
從功能上看,三次握手是最有效的也是最可靠的建立連接的方式。
四次揮手
四次揮手即在終止TCP連接的時候,需要客戶端和服務器共發送4各包確認連接的斷開。
整個流程如下圖所示:
第一次揮手:
客戶端在發送的報文中令FIN=1,序列號設置爲u,u等於前面已經傳送過來的數據的最後一個字節的序號加1,可以參考TCP數據傳輸流程。客戶端進入FIN_WAIT_1狀態。
第二次揮手:
服務器在發送的報文中,令ACK=1,確認號置爲u+1,同時設置自己的序列號V,服務器進入CLOSE_WAIT狀態,客戶端在收到信息以後進入FIN_WAIT_2狀態。
在第二次揮手之後,在CLOSE_WAIT的持續時間內,服務端發送的消息客戶端依舊接收。
第三次揮手:
服務器在發送完數據之後,CLOSE_WAIT結束之後,向客戶端發送FIN+ACK包,即報文中FIN=1,ACK=1,同時確認碼還爲u+1,並且設置自己的序列號w。
第三次揮手之後,服務器進入LAST_ACK最後確認階段。
第四次揮手:
客戶端在收到服務器的FIN+ACK包後,向服務器發送ACK包進行確認,即報文中ACK=1,同時確認碼爲w+1,序列號爲u+1。
之後客戶端進入TIME_WAIT狀態,TIME_WAIT結束後CLOSED。
而客戶端在收到確認之後直接CLOSED。
爲什麼是四次?
第一次握手是客戶端告訴服務器自己將要斷開連接。
第二次握手是服務端將剩餘的數據發送給客戶端。
第三次握手發生在服務器發送完剩餘數據之後,向客戶端發送信息,表明數據發送完成,可以斷開連接。
第四次握手:客戶端收到第三次握手的報文後,向服務端確認自己已經收到了最後的信息,可以斷開連接。並進入TIME_WAIT狀態。
首先前3次揮手必須存在,相信大家都能理解,前三次確保服務器將所有信息都已經發出。
我們假設沒有第四次揮手,那麼服務器在不知道客戶端是否收到自己發送的消息的情況下就斷開了連接,就可能造成數據的丟失。
這四次揮手,可以保證TCP是一個可靠的數據流服務。
如果服務器和客戶端同時關閉,四次握手將退化爲3次握手。
TIME_WAIT狀態的意義
TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態,這時因爲我們必須假象網絡是不可靠的,有可能最後一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在客戶端t發送出最後的ACK回覆,但該ACK可能丟失。服務端如果沒有收到ACK,將不斷重複發送FIN片段。所以客戶端不能立即關閉,它必須確認服務端接收到了該ACK。Client會在發送出ACK之後進入到TIME_WAIT狀態。
如果直到2MSL,Client都沒有再次收到FIN,那麼Client推斷ACK已經被成功接收,則結束TCP連接。
正是上面這些機制保證了TCP的可靠服務。