TCP三次握手四次揮手你真的能講明白嗎
什么是TCP
在了解三次握手四次揮手前必須先了解什么是tcp。
TCP是面向連接的,可靠的,基于字節(jié)流的傳輸層協(xié)議。
- 連接:所謂連接其實(shí)是保證可靠性和流量控制的狀態(tài)信息的總和,包括sokict,滑動(dòng)窗口和序列號(hào)。
- 可靠性:tcp通過序列號(hào),重傳機(jī)制,滑動(dòng)窗口等一系列控制機(jī)制保證數(shù)據(jù)的無重復(fù),無丟失,有序的被接受端處理。
- 字節(jié)流:tcp的數(shù)據(jù)是基于字節(jié)流,因此是無邊界,數(shù)據(jù)是可以無限大的,tcp可以通過分片機(jī)制將數(shù)據(jù)有序發(fā)送到接收端。
TCP結(jié)構(gòu)
TCP的頭部在無“選項(xiàng)”字段的情況下是20個(gè)字節(jié)。包括:
- 2字節(jié)的源端口
- 2字節(jié)的目標(biāo)端口
- 4字節(jié)序列號(hào)
- 4字節(jié)確認(rèn)序列號(hào)
- 4位的首部長度
- 6位保留字段
- 6位標(biāo)志位(SYN,ACK,RST,FIN,URG,PSH)
- 2字節(jié)窗口大小
- 2字節(jié)校驗(yàn)和
- 2字節(jié)緊急指針
這里需要說明的是“選項(xiàng)”這個(gè)字段是用來輔助解決可靠性問題的,正是因?yàn)檫@個(gè)字段的長度是不確定的,所以需要“首部長度”這個(gè)字段來表示TCP頭部的長度。
TCP三次握手過程
什么是三次握手
TCP是基于連接的,所以TCP在使用前必須先建立連接,TCP建立連接的過程是基于三次握手的。
- 首先服務(wù)端的應(yīng)用程序監(jiān)聽某個(gè)端口,也就是建立一個(gè)listened狀態(tài)的Socket,服務(wù)端處于listen狀態(tài)。
- 當(dāng)客戶端創(chuàng)建一個(gè)Socket,并調(diào)用connect函數(shù)連接服務(wù)端的時(shí)候,會(huì)向服務(wù)端發(fā)送一個(gè)SYN狀態(tài)為1的tcp報(bào)文,并攜帶自己的隨機(jī)序列號(hào)??蛻舳颂幱趕yn_send狀態(tài)。
- 服務(wù)端接收到SYN報(bào)文后,會(huì)創(chuàng)建一個(gè)連接放入當(dāng)前Socket的半連接隊(duì)列,然后回復(fù)ACK+SYN報(bào)文并攜帶自己的隨機(jī)序列號(hào)和確認(rèn)序列號(hào)(客戶端序列號(hào)+1)。服務(wù)端處于syn_recv狀態(tài)。
- 客戶端接受到服務(wù)端的ack后,經(jīng)過一定處理,會(huì)給服務(wù)端回復(fù)一個(gè)ack報(bào)文,并攜帶確認(rèn)序列號(hào)(服務(wù)端序列號(hào)+1)。此時(shí)客戶端處于establisten狀態(tài)。
- 服務(wù)端收到ack報(bào)文后,服務(wù)端會(huì)把半連接隊(duì)列中的連接放入全連接隊(duì)列。然后處于establisten狀態(tài)。
至此,tcp連接建立完成,注意第三次握手是可以傳輸數(shù)據(jù)的。在這之前不能傳輸數(shù)據(jù)。
為什么是三次握手
一般大家都會(huì)認(rèn)為三次握手是為了保證客戶端和服務(wù)端雙方都能確認(rèn)自身和接收端建立單向連接和保證自身能夠發(fā)送和接受成功數(shù)據(jù)。
這樣答本身也沒有錯(cuò),但是太粗化了。
既然握手是為保證連接的建立,那就要先知道什么是TCP連接。
TCP連接是保證可靠性和流量控制的狀態(tài)信息的總和,包括socket,序列號(hào),滑動(dòng)窗口。
在這里這個(gè)序列號(hào)至關(guān)重要,是保證消息無重復(fù),無丟失,有序的關(guān)鍵,因此這里其實(shí)就是為了保證序列號(hào)的同步。
客戶端給服務(wù)端發(fā)送一個(gè)初始序列號(hào),服務(wù)端回復(fù)syn+ack,就是告訴客戶端序列號(hào)已經(jīng)收到了并且把服務(wù)端的初始序列號(hào)發(fā)送給客戶端,客戶端收到后也要回復(fù)給服務(wù)端表示序列號(hào)已經(jīng)收到,這樣就能保證雙方都能確保序列號(hào)同步。
但是這還不是最重要的原因,最重要的原因是防止歷史連接初始化再次連接。比如有這樣一種情況,客戶端發(fā)送syn包給服務(wù)端,但是網(wǎng)絡(luò)阻塞了,服務(wù)端沒有收到,所以服務(wù)端也不會(huì)回復(fù),客戶端收不到回復(fù)就會(huì)重新發(fā)送syn包,但是就在這時(shí)候服務(wù)端接收到了第一個(gè)syn包,并且回復(fù)客戶端,這個(gè)時(shí)候客戶端會(huì)進(jìn)行比對(duì)校驗(yàn)這是不是自己最新發(fā)送的syn回復(fù)包,如果不是的話就會(huì)給服務(wù)端發(fā)送rst包,表示要求服務(wù)端中斷這個(gè)連接。這也是三次握手的意義所在。
如果說沒有第三次握手,那么在發(fā)生上面的這個(gè)情況后,服務(wù)端就會(huì)為每個(gè)syn請(qǐng)求創(chuàng)建連接,連接是需要占用內(nèi)存的,就會(huì)耗費(fèi)很多的資源。造成資源浪費(fèi),所以三次握手很有必要。
那么四次握手是否可以呢?
四次握手的話也是可以的,四次握手其實(shí)就是客戶端發(fā)送syn包給服務(wù)端,服務(wù)端回復(fù)ack包,服務(wù)端發(fā)送syn包給客戶端,客戶端回復(fù)ack包,三次握手中的第二次握手回復(fù)的是syn+ack包,所有相當(dāng)于合并了四次握手中的中間兩次,所以三次握手最好。
四次揮手的過程
TCP是雙向連接,所以兩個(gè)方向上的連接都要斷開。
- 斷開前客戶端和服務(wù)端都處于ESTABLISTENED狀態(tài)。
- 客戶端調(diào)用close方法盡心斷開連接操作,客戶端會(huì)發(fā)送fin包給服務(wù)端??蛻舳颂幱趂in_wait1狀態(tài)
- 服務(wù)端接收到fin包后,會(huì)回復(fù)一個(gè)ack。此時(shí)服務(wù)端處于closed_wait狀態(tài)。
- 客戶端收到服務(wù)端的ack后,表示已經(jīng)斷開了自己到服務(wù)端的連接,但是服務(wù)端到客戶端的連接還沒有斷開,客戶端需要等待服務(wù)端主動(dòng)請(qǐng)求斷開。此時(shí)客戶端處于fin_wait2狀態(tài)。
- 服務(wù)端之所以不會(huì)立刻給客戶端發(fā)送fin包是因?yàn)榉?wù)端可能還存在要發(fā)送的數(shù)據(jù),所以服務(wù)端需要把要處理的數(shù)據(jù)處理完在發(fā)送fin包給客戶端,此時(shí)數(shù)據(jù)已經(jīng)處理完,服務(wù)端主動(dòng)給客戶端發(fā)送fin包,此時(shí)服務(wù)端處于last_ack狀態(tài)。
- 客戶端收到fin包后,會(huì)回復(fù)ack給服務(wù)端,此時(shí)客戶端處于time_wait狀態(tài)。
- 服務(wù)端收到ack后將狀態(tài)置為close.
- 客戶端此時(shí)并不會(huì)直接進(jìn)入close狀態(tài),而是會(huì)進(jìn)入time_wait狀態(tài), 這個(gè)狀態(tài)會(huì)持續(xù)2MSL時(shí)間。
在網(wǎng)絡(luò)傳輸?shù)氖澜缋?,有兩個(gè)值是用來表示數(shù)據(jù)包失效的:
- MSL是報(bào)文在網(wǎng)絡(luò)中的最大存活時(shí)間,超過這個(gè)時(shí)間就會(huì)被丟棄。
- TTL:在ip層的頭部中有一個(gè)TTL字段保存所經(jīng)過的路由數(shù),沒經(jīng)過一個(gè)路由數(shù)就會(huì)減1,當(dāng)為0的時(shí)候,數(shù)據(jù)就會(huì)被丟棄。
所以一般情況下MSL會(huì)大于TTL減為0所消耗的時(shí)間。
這里為什么是2倍的MSL呢?
因?yàn)楫?dāng)客戶端接收到服務(wù)端的fin包后,會(huì)向服務(wù)端回復(fù)ack,但是客戶端不知道這個(gè)ack是否發(fā)送成功了,所以客戶端需要確認(rèn)服務(wù)端接受成功后才能置為close狀態(tài),怎么確認(rèn)呢,因?yàn)槭≈貍鳈C(jī)制的存在,如果因?yàn)榫W(wǎng)絡(luò)阻塞服務(wù)端沒有收到ack,服務(wù)端會(huì)再次發(fā)送一次fin,一次ack包和再一次fin包就是2倍的MSL。MSL的計(jì)時(shí)是從收到fin包并且發(fā)送ack包開始的。
除了上面說的保證客戶端的ack發(fā)送到服務(wù)端,并被正確接收,從而保證被關(guān)閉連接的一方可以正確關(guān)閉。
還能保證那些阻塞在網(wǎng)絡(luò)中舊的連接,在端口又被復(fù)用的情況下,被接收到,這樣就會(huì)發(fā)生數(shù)據(jù)錯(cuò)亂,而time_wait可以保證全部的網(wǎng)絡(luò)中的連接被丟棄。
MSL默認(rèn)是30秒。需要注意的是time_wait 的狀態(tài)多了以后會(huì)占用內(nèi)存資源和端口資源,所以不宜太多。
為什么要進(jìn)行四次揮手?
tcp是雙向連接,客戶端發(fā)送fin包給服務(wù)端,服務(wù)端回復(fù)ack,只是客戶端告訴服務(wù)端不再向服務(wù)端發(fā)送數(shù)據(jù)。
還需要服務(wù)端告訴客戶端,服務(wù)端不再向客戶端發(fā)送數(shù)據(jù)了,也就是服務(wù)端也要想客戶端發(fā)送fin包,客戶端也要給服務(wù)端回復(fù)ack包,這時(shí)候服務(wù)端和客戶端才能進(jìn)入close狀態(tài)。
服務(wù)端在收到客戶端發(fā)送的fin包并回復(fù)ack包后,服務(wù)端并不能馬上向發(fā)送端發(fā)送fin包,因?yàn)榇藭r(shí)可能還有連接在處理數(shù)據(jù),必須等到數(shù)據(jù)處理完后才能向客戶端發(fā)送fin包。
正因?yàn)檫@個(gè)原因,不能像三次握手那樣把中間兩次合并。