關(guān)于 TCP(Transmission Control Protocol) 三次握手這個(gè)問(wèn)題赊级,基本上就是面試必問(wèn),是一個(gè)非常熱門(mén)的問(wèn)題岔绸。先當(dāng)初我找實(shí)習(xí)的時(shí)候也被問(wèn)到了理逊,當(dāng)那個(gè)時(shí)候只能大概說(shuō)一下過(guò)程,一些標(biāo)記位什么的還是不懂盒揉。
今天我們就大概講一下晋被,應(yīng)付面試應(yīng)該可以了。
標(biāo)記位
我們這里先說(shuō)要一下標(biāo)記位和標(biāo)記位的含義刚盈,這有助于后面理解整個(gè)握手的過(guò)程羡洛。
我們先來(lái)看一下 TCP 報(bào)文的格式吧。
我們要講 TCP 握手的過(guò)程藕漱,就要先了解圖中紅色框出來(lái)的幾個(gè)信息欲侮,下面我們一一解釋。
序列號(hào)(Sequence number)
占4個(gè)字節(jié)肋联,用來(lái)標(biāo)記數(shù)據(jù)段的順序威蕉,TCP 把連接中發(fā)送的所有數(shù)據(jù)字節(jié)都編上一個(gè)序號(hào),第一個(gè)字節(jié)的編號(hào)由本地隨機(jī)產(chǎn)生橄仍;給字節(jié)編上序號(hào)后韧涨,就給每一個(gè)報(bào)文段指派一個(gè)序號(hào);序列號(hào)seq就是這個(gè)報(bào)文段中的第一個(gè)字節(jié)的數(shù)據(jù)編號(hào)侮繁。
我們這里也補(bǔ)充一點(diǎn)網(wǎng)絡(luò)的知識(shí)吧虑粥,我們都知道 TCP 是屬于傳輸層,要保障數(shù)據(jù)準(zhǔn)確完整的傳輸鼎天,而在網(wǎng)絡(luò)傳輸中舀奶,一般都是要分包的,因?yàn)槿绻环职洌菙?shù)據(jù)丟失了但荤,要重新傳的代價(jià)太大了,我們分成一小段一小段進(jìn)行傳輸涧至,這樣即使有一小段丟失了腹躁,我們就只要重新傳這一小段就可以了,可以節(jié)省很多流量南蓬。
TCP 在傳輸數(shù)據(jù)的時(shí)候纺非,會(huì)順便把數(shù)據(jù)放到重發(fā)隊(duì)列里面,然后啟動(dòng)計(jì)時(shí)器赘方,如果后面接收到了這個(gè)包的確認(rèn)信息烧颖,就把這個(gè)數(shù)據(jù)包從隊(duì)列里面刪除掉,如果計(jì)時(shí)器超時(shí)了都還沒(méi)有收到確認(rèn)信息窄陡,就重新發(fā)送這個(gè)數(shù)據(jù)包炕淮,因?yàn)閿?shù)據(jù)可能丟失了,對(duì)面沒(méi)有收到跳夭。另外因?yàn)橛行蛄刑?hào)的存在涂圆,在接受到全部數(shù)據(jù)之后就可以按照順序重新組裝起來(lái)了,保障了數(shù)據(jù)的準(zhǔn)確性和完整性币叹。
說(shuō)人話润歉,就是有點(diǎn)類似 ID 一般,給這次連接定一個(gè)序列號(hào)颈抚。后面?zhèn)鬏敂?shù)據(jù)就從這個(gè)序號(hào)開(kāi)始踩衩。
后面我們就用 seq 來(lái)表示 序列號(hào)。
ACK (Acknowledgement)
占1位邪意,僅當(dāng) ACK=1 時(shí)九妈,確認(rèn)號(hào)字段才有效反砌。ACK=0時(shí)雾鬼,確認(rèn)號(hào)無(wú)效。
說(shuō)人話宴树,ACK 就是相當(dāng)于一個(gè)開(kāi)關(guān)策菜,確認(rèn)號(hào) 就相當(dāng)于燈泡的顏色,無(wú)論你這個(gè)燈泡是什么顏色酒贬,如果開(kāi)關(guān)不打開(kāi)又憨,就什么都看不到,也就是說(shuō)無(wú)效锭吨。ACK=1 時(shí)蠢莺,這個(gè)開(kāi)關(guān)就打開(kāi)了,ACK=0時(shí)零如,這個(gè)開(kāi)關(guān)就是關(guān)閉了躏将。這樣是不是好理解多了锄弱。
ack(Acknowledge number)
剛才我們說(shuō)了,確認(rèn)號(hào)收 ACK 控制祸憋,只有 ACK=1 時(shí)会宪,確認(rèn)號(hào)才有效。
那問(wèn)題又來(lái)了蚯窥,這個(gè)確認(rèn)號(hào)是干什么用的呢掸鹅?
確認(rèn)號(hào)就是用來(lái)確認(rèn)的,比如就好像你微信發(fā)了一個(gè)文件給我拦赠,我回復(fù)你一句“收到”是同樣的道理巍沙,就是用來(lái)跟對(duì)方確認(rèn)已經(jīng)收到了消息。
后面我們就用 ack 來(lái)表示 確認(rèn)號(hào)荷鼠。
SYN(synchronous)
這個(gè)信號(hào)也是很關(guān)鍵赎瞎,SYN 的意思就是說(shuō)請(qǐng)求同步,也就是請(qǐng)求連接的意思颊咬。
三次握手
說(shuō)完了幾個(gè)關(guān)鍵的標(biāo)記信息务甥,我們就開(kāi)始來(lái)說(shuō) TCP 建立的三次握手。
- 主機(jī)A 向 主機(jī)B 發(fā)送一個(gè)信號(hào)喳篇,【SYN=1敞临,ACK=0,seq=x】麸澜,這個(gè)信號(hào)的意思就是說(shuō)“主機(jī)B挺尿,我想跟你建立連接”。主機(jī)A 就會(huì)進(jìn)入 SYN-SENT 狀態(tài)炊邦。
- 主機(jī)B 收到 主機(jī)A 的請(qǐng)求之后编矾,就會(huì)做出判斷,如果同意的話馁害,就會(huì)回一個(gè)消息 【SYN=1窄俏,ACK=1,ack=x+1碘菜,seq=y】凹蜈,意思是說(shuō),你的請(qǐng)求我收到了忍啸,我也想跟你建立連接仰坦。主機(jī)B 的狀態(tài)由 LISTEN 進(jìn)入 SYN-RCVD 狀態(tài)。
- 主機(jī)A 收到 主機(jī)B 的響應(yīng)后计雌,如果要建立連接就會(huì)回一個(gè)消息 【SYN=0悄晃,ACK=1,ack=y+1凿滤,seq=x+1】妈橄,表示鼠渺,我知道了,我要發(fā)送數(shù)據(jù)了眷细。主機(jī)A 進(jìn)入 ESTABLISHED 狀態(tài)拦盹。
到此,TCP 通道就會(huì)建立了溪椎,后面就通過(guò)這個(gè) TCP 通道傳輸數(shù)據(jù)了普舆。為了方便大家理解,我從網(wǎng)上找了一個(gè)圖校读。
怎么樣沼侣?聽(tīng)起來(lái)不會(huì)很復(fù)雜吧。
如果還不是很理解歉秫,我們就再場(chǎng)景化一下蛾洛,想當(dāng)年我實(shí)習(xí)面試的時(shí)候就是這么回答的,哈哈哈雁芙。
先模擬一個(gè)場(chǎng)景轧膘,A 要通過(guò) 微信 向 B 發(fā)一個(gè)文件。因?yàn)椴恢缹?duì)面有沒(méi)有看微信兔甘,所以就要先問(wèn)谎碍。
- 主機(jī)A 向 主機(jī)B 說(shuō),“嘿洞焙,我等一下要給你發(fā)個(gè)東西蟆淀,你看到了沒(méi)有?”澡匪。
- 主機(jī)B 看到之后熔任,就回答說(shuō),“哦唁情,好的疑苔,我知道了。你要發(fā)前先跟我說(shuō)一聲荠瘪,我等著呢夯巷。“
- 主機(jī)A 看到了說(shuō)哀墓,”O(jiān)K,我這就發(fā)了“喷兼。
這樣就不會(huì)那么抽象了吧篮绰,哈哈哈〖竟撸可惜不會(huì)做圖吠各,不然就做兩個(gè)圖臀突,以后再學(xué)學(xué)做圖。
三次握手引申出的問(wèn)題
為什么要三次握手贾漏,為什么不能是一次或者兩次候学?
這個(gè)問(wèn)題也是非常常見(jiàn)的問(wèn)題。三次握手是確認(rèn)雙方都在線纵散、都準(zhǔn)備好發(fā)送和接受數(shù)據(jù)梳码,而且連接性能最優(yōu)的方式。
如果是一次握手根本就不可能伍掀,因?yàn)楦静豢赡艽_認(rèn)對(duì)方在線掰茶,不斷重發(fā)只會(huì)浪費(fèi)性能。
如果是兩次握手蜜笤,那服務(wù)器收到客戶端一個(gè)連接請(qǐng)求就會(huì)建立連接濒蒋。但是如果服務(wù)器給客戶端的響應(yīng)丟失了,那客戶端就根本不知道服務(wù)器接收到這個(gè)數(shù)據(jù)沒(méi)有把兔,就會(huì)一直等待沪伙,另一方面,服務(wù)器也在等客戶端發(fā)送數(shù)據(jù)县好,現(xiàn)在兩邊就都在等待對(duì)方焰坪,那建立的這條 TCP 通道就白白浪費(fèi)了。
同樣的聘惦,如果客戶端一開(kāi)始發(fā)送的連接請(qǐng)求由于網(wǎng)絡(luò)延遲的問(wèn)題某饰,一直沒(méi)有到服務(wù)器,客戶端就會(huì)廢棄第一次的請(qǐng)求善绎,重新發(fā)送一個(gè)新的連接請(qǐng)求黔漂,服務(wù)器接收到之后就會(huì)建立連接。這樣看似沒(méi)什么問(wèn)題禀酱,但要是第一次后面又到達(dá)服務(wù)器了呢炬守?由于只要兩次握手,服務(wù)器接收到第一次請(qǐng)求之后剂跟,回一個(gè)消息就可以建立 TCP 連接了减途,但是客戶端判定第一次的請(qǐng)求已經(jīng)廢棄了,也不會(huì)跟服務(wù)器建立 TCP 連接曹洽,也更不會(huì)發(fā)數(shù)據(jù)了鳍置,服務(wù)器就也白白浪費(fèi)了這個(gè) TCP 連接。
如果在第三次握手的時(shí)候送淆,數(shù)據(jù)丟失税产、服務(wù)器沒(méi)有收到怎么辦?
如果在第三次握手的時(shí)候,數(shù)據(jù)丟失辟拷,服務(wù)器沒(méi)有收到撞羽,但客戶端認(rèn)為 TCP 連接已經(jīng)建立了,開(kāi)始發(fā)數(shù)據(jù)會(huì)有問(wèn)題嗎衫冻?
有問(wèn)題诀紊,這個(gè)時(shí)候客戶端就會(huì)收到服務(wù)器的 RST 應(yīng)答,客戶端就知道出現(xiàn)問(wèn)題了隅俘,需要重新連接邻奠。
三次握手的核心
其實(shí)三次握手的主要核心就是確認(rèn)通信雙方都在線能夠,而且不會(huì)出現(xiàn)互相等待的情況考赛。
如果只有一次握手惕澎,根本不能確認(rèn)對(duì)方在線。
如果是兩次握手颜骤,在理想情況下是能夠確立的唧喉,但是如果網(wǎng)絡(luò)出現(xiàn)問(wèn)題,就會(huì)導(dǎo)致死鎖的情況忍抽。浪費(fèi)性能八孝。
四次握手
既然三次握手都講完了,我們就加個(gè)餐鸠项,說(shuō)一下四次握手吧干跛。
四次握手是用來(lái)斷開(kāi) TCP 連接的。
為什么建立連接的時(shí)候只要三次祟绊,斷開(kāi)卻要四次楼入?
我們同樣先來(lái)講一下用到的一個(gè)關(guān)鍵標(biāo)記信息。
FIN(Finish)
FIN 標(biāo)記位牧抽,表示數(shù)據(jù)發(fā)送完畢(Finish)嘉熊,也就是請(qǐng)求斷開(kāi)連接。
斷開(kāi)連接 -- 四次握手
其實(shí)跟建立連接時(shí)的三次握手不同扬舒,主要因?yàn)椴簦@個(gè)時(shí)候連接已經(jīng)建立了,有一方主動(dòng)請(qǐng)求斷開(kāi)連接讲坎,但是被動(dòng)方可能還有一些數(shù)據(jù)正在發(fā)送孕惜,或者還有些數(shù)據(jù)沒(méi)發(fā)送,不能馬上中斷晨炕,要等這部分?jǐn)?shù)據(jù)發(fā)送完衫画。
下面說(shuō)一下四次握手的過(guò)程吧。
- 主機(jī)A 向 主機(jī)B 發(fā)送一個(gè)關(guān)閉連接請(qǐng)求府瞄”贪酰【FIN=1碘箍,seq=u】,狀態(tài)由 ESTABLISHED 進(jìn)入 FIN-WAIT-1 遵馆。
- 主機(jī)B 接收到關(guān)閉請(qǐng)求鲸郊,會(huì)響應(yīng) 【ACK=1,seq=v货邓,ack=u+1】秆撮,告訴 主機(jī)A,你的請(qǐng)求是收到了换况,但是等一下职辨,我還有東西沒(méi)發(fā)完。狀態(tài)由 ESTABLISHED 進(jìn)入 CLOSE-WAIT 戈二。主機(jī)A 收到之后舒裤,就會(huì)進(jìn)入 FIN-WAIT-2 狀態(tài)。
- 主機(jī)B 等發(fā)送完剩余的數(shù)據(jù)之后觉吭,發(fā)送 【FIN=1腾供,ACK=1,ack=u+1鲜滩,seq=w】伴鳖,然后就進(jìn)入 LAST-ACK 狀態(tài)。這里 seq 為什么是變了呢徙硅?因?yàn)閯偛虐咽S嗟臄?shù)據(jù)發(fā)送出去了榜聂,seq 增加了。
- 主機(jī)A 接受到消息之后嗓蘑,回復(fù) 【ACK=1须肆,seq=u+1,ack=w+1】桩皿,然后就進(jìn)入 TIME-WAIT 狀態(tài)豌汇。等待 兩倍最長(zhǎng)報(bào)文壽命(2MSL)的時(shí)間之后,就進(jìn)入 CLOSE 狀態(tài)业簿。主機(jī)B 接受到之后瘤礁,就也進(jìn)入 CLOSE 狀態(tài)。
我也在網(wǎng)上找了一個(gè)圖梅尤,方便大家理解柜思。
整個(gè)流程大概就是這樣了,如果覺(jué)得還不錯(cuò)巷燥,麻煩點(diǎn)個(gè)贊再走吧赡盘。
也可以轉(zhuǎn)發(fā)給那些不太了解這塊的同學(xué),可以好好嘲諷一下學(xué)習(xí)一下缰揪,嘿嘿陨享。
如果發(fā)現(xiàn)有問(wèn)題葱淳,麻煩各位大佬在評(píng)論區(qū)指出。