三次握手和四次揮手到底是個什么鬼東西?
本文轉(zhuǎn)載自微信公眾號「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請聯(lián)系Java極客技術(shù)公眾號。
之前總有是有面試官喜歡問,你知道什么是三次握手么?什么是四次揮手么?為什么握手需要三次,揮手需要四次呢?今天我們就來詳細(xì)的聊一下這個。
1.什么是TCP
TCP協(xié)議,簡單稱呼一下的話,那就是傳輸控制協(xié)議,為什么這么稱呼它呢,因為這個協(xié)議就是用來對數(shù)據(jù)的傳輸進(jìn)行控制的一個協(xié)議,這個大家肯定也都是沒有異議的。
TCP有時候你會在很多書中看它們稱之為“套接字”,其實這就是翻譯,在原著中的意思可能就是 a place on a surface or machine with holes for connecting a piece of electrical equipment.,然后經(jīng)過翻譯的手,翻譯過來就是套接字的意思,其實大家心里清楚就行,問這個的幾乎不怎么存在。所以大家就是知道就可以了。
我們也都知道網(wǎng)絡(luò)協(xié)議是分層的,7層(5層),可以分為不標(biāo)準(zhǔn)的7層,也可以分為標(biāo)準(zhǔn)的五層(也有人說是4層,區(qū)別不大,就少了一個物理層面),實際上這個標(biāo)準(zhǔn)阿粉個人感覺還是籠統(tǒng)的和細(xì)微的差別就像這個圖。
而這個分層的概念則是不同的,如果說你是按照OSI七層模型結(jié)構(gòu)體來分,那就是7層,如果是按照TCP/IP,那么就是4層。在這里的TCP,就是在我們的數(shù)據(jù)傳輸層里面,因為畢竟阿粉之前就說了,傳輸控制協(xié)議嘛。
2.TCP協(xié)議的報文
而在TCP/IP的分層中,就算是數(shù)據(jù)傳輸層,那也是有著不是TCP協(xié)議的存在的,比如說還有UDP,就像下圖。
TCP和UDP是兩種最為著名的運(yùn)輸層協(xié)議,二者都使用IP作為網(wǎng)絡(luò)層協(xié)議。
盡管TCP使用的是不可靠的IP服務(wù),但是它提供的傳輸層服務(wù),卻是更加可靠的。
那么我們就先來看看這個TCP協(xié)議的報頭是什么樣子的,把抽象的東西具體化一點(diǎn),才能更加的加深理解。
TCP數(shù)據(jù)被封裝在一個IP數(shù)據(jù)報中,就像上圖所示,而我們需要分析的,就是其中的TCP數(shù)據(jù)報中。
每個TCP段都包含源端和目的端的端口號,用于尋找發(fā)端和收端應(yīng)用進(jìn)程。這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連接。
這個時候我們就得來看看里面都有些什么東西了,
- 16位源端口號和16位目的端口號:其實就相當(dāng)于是一個插口,也可以稱之為數(shù)據(jù)的來源進(jìn)程和目的進(jìn)程
- 32位序號:序號用來標(biāo)識從T C P發(fā)端向TCP收端發(fā)送的數(shù)據(jù)字節(jié)流,它表示在這個報文段中的的第一個數(shù)據(jù)字節(jié)
- 4位首部長度:表示該tcp報頭有多少個4字節(jié)(32個bit)
- 6位標(biāo)志位:這個是重頭戲
U R G 緊急指針有效
A C K 確認(rèn)序號有效
P S H 接收方應(yīng)該盡快將這個報文段交給應(yīng)用層
R S T 重建連接
S Y N 同步序號用來發(fā)起一個連接。這個標(biāo)志和下一個標(biāo)志將在第 1 8章介紹
F I N 發(fā)端完成發(fā)送任務(wù)
- 16位窗口大?。捍翱诖笮樽止?jié)數(shù),起始于確認(rèn)序號字段指明的值,這個值是接收端正期望接收的字節(jié)。
- 16位緊急指針:主要是看什么數(shù)據(jù)是緊急的
- 16位檢驗和:16位檢驗和覆蓋了整個的TCP報文段:TCP首部和TCP數(shù)據(jù)。這是一個強(qiáng)制性的字段,一定是由發(fā)端計算和存儲,并由收端進(jìn)行驗證。
3.TCP的三次握手連接是什么樣子的
既然我們文章要說的是TCP的三次握手,和四次揮手,那么肯定是說的連接,也不是說的其他的。那么它這個連接的過程說的是什么呢?
我們還是從圖中理解,這樣比較好理解,
- TCP第一次握手:服務(wù)端的TCP進(jìn)程先創(chuàng)建傳輸控制塊TCB,準(zhǔn)備接受客戶端進(jìn)程的連接請求,然后服務(wù)端進(jìn)程處于LISTEN狀態(tài),等待客戶端的連接請求,向服務(wù)端發(fā)出連接請求報文段,該報文段首部中的SYN=1,ACK=0,同時選擇一個初始序號 seq=i。TCP規(guī)定,SYN=1的報文段不能攜帶數(shù)據(jù),但要消耗掉一個序號。這時,TCP客戶進(jìn)程進(jìn)入SYN—SENT(同步已發(fā)送)狀態(tài)。
簡單的來說SYN—SENT狀態(tài),同步已發(fā)送狀態(tài),這是第一次握手的時候的狀態(tài)。
- TCP第二次握手:服務(wù)端收到客戶端發(fā)來的請求報文后,如果同意建立連接,則向客戶端發(fā)送確認(rèn)。確認(rèn)報文中的SYN=1,ACK=1,確認(rèn)號ack=i+1,同時為自己 選擇一個初始序號seq=j。同樣該報文段也是SYN=1的報文段,不能攜帶數(shù)據(jù),但同樣要消耗掉一個序號。這時,TCP服務(wù)端進(jìn)入SYN—RCVD(同步收到)狀態(tài)
這個第二次握手就會進(jìn)入到同步收到狀態(tài)。
- TCP第三次握手:客戶端進(jìn)入ESTABLISHED(已建立連接)狀態(tài),TCP客戶端進(jìn)程收到服務(wù)端進(jìn)程的確認(rèn)后,還要向服務(wù)端給出確認(rèn)。確認(rèn)報文段的ACK=1,確認(rèn)號ack=j+1,而自己的序號為seq=i+1。TCP的標(biāo)準(zhǔn)規(guī)定,ACK報文段可以攜帶數(shù)據(jù),但如果不攜帶數(shù)據(jù)則不消耗序號,因此,如果不攜帶數(shù)據(jù),則下一個報文段的序號仍為seq=i+1。
而當(dāng)?shù)谌挝帐诌B接完成的時候,已經(jīng)標(biāo)志了現(xiàn)在是已經(jīng)完全的建立了連接,而這個時候就可以進(jìn)行數(shù)據(jù)傳遞了。
有時候就有人會問了,為什么是三次握手而不是兩次,也不是四五次呢?一般情況下問這種問題的都會是面試官,如果你在面試過程中已經(jīng)把這個三次握手說完了之后,他有時候就會問這種問題,讓你談?wù)勀阕约旱睦斫?,那么為什么?
在RFC 793 指出的 TCP 連接使用三次握手的首要原因:he principle reason for the three-way handshake is to prevent old duplicate connection initiations from causing confusion.
翻譯過來就是三次握手的主要原因是防止舊的重復(fù)連接啟動引起混淆。
也就是說,如果客戶端連續(xù)發(fā)出多個SYN建立連接的報文的話,在網(wǎng)絡(luò)擁堵的情況就會出現(xiàn),一個「舊 SYN 報文」比「最新的 SYN 」 報文早到達(dá)了服務(wù)端,那么此時服務(wù)端就會回一個 SYN + ACK 報文給客戶端,客戶端收到后可以根據(jù)自身的上下文,判斷這是一個歷史連接(序列號過期或超時),那么客戶端就會發(fā)送 RST 報文給服務(wù)端,表示中止這一次連接。
而如果是兩次握手,那么完蛋了,這時候判斷不出這個連接是不是歷史連接,中斷還是不中斷,這就沒辦法處理了,而三次握手就可以在客戶端進(jìn)行第三次發(fā)送報文的時候,有足夠的上下文來判斷這個連接到底是否屬于歷史連接。
那么為什么不是四次連接呢?大家可以繼續(xù)翻到上面的圖,如果是四次連接,那么也就是說,把ACK和SYN進(jìn)行了分開,seq=y和ack=x+1這兩步進(jìn)行了分開,雖然四次握手也能夠完成這一步,但是為了省事,人家還是三部就做完了,這樣一來,也能確保雙方的初始序列號能被可靠的同步,何必在多費(fèi)一步操作呢?
4.TCP連接的四次揮手
既然我們TCP連接的時候進(jìn)行了三次握手,為什么要中斷的時候,我們要進(jìn)行四次揮手呢?這還是得從圖中來理解這個事情。
- 客戶端A發(fā)送一個FIN,用來關(guān)閉客戶A到服務(wù)器B的數(shù)據(jù)傳送
- 服務(wù)器B收到這個FIN,它發(fā)回一個ACK,確認(rèn)序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將占用一個序號。
- 服務(wù)器B關(guān)閉與客戶端A的連接,發(fā)送一個FIN給客戶端A
- 客戶端A發(fā)回ACK報文確認(rèn),并將確認(rèn)序號設(shè)置為收到序號加1
那么為什么是四次呢?之前阿粉面試別人的時候,有個哥們給我了一句話,由于TCP連接是全雙工的,因此每個方向都必須單獨(dú)進(jìn)行關(guān)閉。這話比較籠統(tǒng),但是沒解釋清楚為啥要做四次揮手,這是最尷尬的,然后問詳細(xì)的樣子是什么樣的,他也解答的不錯。
那么為什么呢?
這是因為服務(wù)端的LISTEN狀態(tài)下的SOCKET當(dāng)收到SYN報文的建連請求后,它可以把ACK和SYN(ACK起應(yīng)答作用,而SYN起同步作用)放在一個報文里來發(fā)送。但關(guān)閉連接時,當(dāng)收到對方的FIN報文通知時,它僅僅表示對方?jīng)]有數(shù)據(jù)發(fā)送給你了;但未必你所有的數(shù)據(jù)都全部發(fā)送給對方了,所以你可以未必會馬上會關(guān)閉SOCKET,也即你可能還需要發(fā)送一些數(shù)據(jù)給對方之后,再發(fā)送FIN報文給對方來表示你同意可以關(guān)閉連接了,所以它這里的ACK報文和FIN報文多數(shù)情況下都是分開發(fā)送的。
也就是說,在關(guān)閉的時候,為了確認(rèn)是否關(guān)閉連接,ACK的報文和FIN的報文是進(jìn)行分開發(fā)送,而這時候,揮手的次數(shù)也就從三次變成了4次,這樣是不是就好理解一點(diǎn)了。
5. 文章參考
- 《TCP/IP詳解》
- 《詳解 TCP 連接的“ 三次握手 ”與“ 四次揮手 ”》
- 《TCP-IP詳解卷一:協(xié)議》
- 《JAVA網(wǎng)絡(luò)編程(第3版)》