問題發(fā)生場(chǎng)景
在做app客戶端tcp掉線自動(dòng)重連的時(shí)候,我把一個(gè) socket 對(duì)象在應(yīng)用層斷開了,然后我考慮到內(nèi)存碎片問題(內(nèi)存的內(nèi)部碎片司致,外部碎片)拆吆,就沒有釋放這個(gè)socket,而是直接用它再去連接其他服務(wù)器脂矫,發(fā)現(xiàn)連不上枣耀,我向這個(gè)socket寫任何數(shù)據(jù)都沒有反應(yīng)!然而我申請(qǐng)一個(gè)新的socket去連接羹唠,又一切正常奕枢。
為什么向斷開的socket再寫數(shù)據(jù)就沒有任何回應(yīng)了呢娄昆?
原因
經(jīng)過一番查找終于找到了原因佩微,socket斷開之后,會(huì)進(jìn)入一個(gè)叫TIME_WAIT的狀態(tài)萌焰,該狀態(tài)會(huì)持續(xù)4到5分鐘哺眯,在這個(gè)狀態(tài)內(nèi),socket不會(huì)收發(fā)任何來自應(yīng)用層的數(shù)據(jù)扒俯,看下圖(圖片來源于網(wǎng)絡(luò))
看上圖奶卓,客戶端在發(fā)出最后一個(gè)ack包之后,進(jìn)入了 TIME_WAIT 狀態(tài)撼玄!
因?yàn)樽詈笠粋€(gè) ack 可能會(huì)丟失夺姑,如果丟失后會(huì)觸發(fā)服務(wù)器tcp超時(shí)重傳機(jī)制,導(dǎo)致服務(wù)器再發(fā)個(gè) FIN 包給你掌猛,所以要進(jìn)入 TIME_WAIT 等待4分鐘盏浙,確保網(wǎng)絡(luò)上沒有殘余的 FIN 包,才能進(jìn)入 CLOSED 狀態(tài)荔茬,進(jìn)行下一次連接废膘。在 TIME_WAIT 狀態(tài)中不能建立新的連接,假設(shè)你建立新的連接慕蔚,可能會(huì)收到服務(wù)器超時(shí)來的FIN包丐黄,會(huì)斷掉的。
為什么是等待4分鐘孔飒,因?yàn)橛袀€(gè)最大生存時(shí)間MSL灌闺,最后一個(gè)ack包過去需要一個(gè)MSL,服務(wù)器重傳一個(gè)FIN包需要一個(gè)MSL坏瞄,加起來就是 2MSL桂对,一個(gè)MSL大約2分鐘,兩個(gè)就是4分鐘惦积。所以4分鐘后接校,tcp就能夠確定網(wǎng)絡(luò)上沒有FIN包了,這時(shí)socket才可以發(fā)起新的連接。
簡(jiǎn)單說說4次揮手蛛勉,3次揮手
既然上了張4次揮手的圖鹿寻,那得順帶說說吧。
為什么是4次揮手诽凌?
因?yàn)閠cp是全雙工的毡熏,有收,發(fā)兩個(gè)通道侣诵,關(guān)閉一個(gè)通道需要2次痢法,關(guān)閉兩個(gè)就需要4次了。
客戶端發(fā)出FIN包杜顺,客戶端進(jìn)入FIN-WAIT-1狀態(tài)
服務(wù)器收到FIN包财搁,服務(wù)器進(jìn)入CLOSE-WAIT狀態(tài)
剩下的狀態(tài)看上面那個(gè)圖吧,最后主動(dòng)關(guān)閉socket的一方會(huì)進(jìn)入TIME_WAIT躬络,最終雙方都CLOSE了尖奔,完成4次揮手。
既然說了4次揮手穷当,3次握手也得順帶說說吧
為什么是3次握手提茁?
首先完全可靠的通信并不存在。
- 3次握手可以基本排除兩方通信設(shè)備的故障馁菜。(一方的收或發(fā)有故障了茴扁,3次握手也完成不了,自己想想是不是)
- 3次握手可以保護(hù)接收方的資源汪疮。(接收方要給發(fā)起方準(zhǔn)備socket峭火,準(zhǔn)備線程空間,要消耗系統(tǒng)資源的铲咨,不敢隨便建立躲胳,必須要發(fā)起方兩次確定在線后才能建立連接)
- 防止殘余的包建立新的請(qǐng)求