收到RST,就一定會(huì)斷開TCP連接嗎?
就是要搞一大堆原理性的東西,再回答標(biāo)題的問題。說(shuō)這個(gè)是因?yàn)槲疫@次會(huì)把問題的答案就放到開頭嗎?不!我就不!但是大家可以直接根據(jù)目錄看自己感興趣的部分。之所以要先鋪墊一些原理,還是希望大家能先看些基礎(chǔ)的,再慢慢循序漸進(jìn),這樣有利于建立知識(shí)體系。多一點(diǎn)上下文,少一點(diǎn)
gap
。好了,進(jìn)入正題。下面是這篇文章的目錄。什么是RST
我們都知道TCP正常情況下斷開連接是用四次揮手,那是正常時(shí)候的優(yōu)雅做法。但異常情況下,收發(fā)雙方都不一定正常,連揮手這件事本身都可能做不到,所以就需要一個(gè)機(jī)制去強(qiáng)行關(guān)閉連接。RST 就是用于這種情況,一般用來(lái)異常地關(guān)閉一個(gè)連接。它是一個(gè)TCP包頭中的標(biāo)志位。正常情況下,不管是發(fā)出,還是收到置了這個(gè)標(biāo)志位的數(shù)據(jù)包,相應(yīng)的內(nèi)存、端口等連接資源都會(huì)被釋放。從效果上來(lái)看就是TCP連接被關(guān)閉了。而接收到 RST的一方,一般會(huì)看到一個(gè)connection reset
或 ?connection refused
的報(bào)錯(cuò)。怎么知道收到RST了?
我們知道內(nèi)核跟應(yīng)用層是分開的兩層,網(wǎng)絡(luò)通信功能在內(nèi)核,我們的客戶端或服務(wù)端屬于應(yīng)用層。應(yīng)用層只能通過send/recv
與內(nèi)核交互,才能感知到內(nèi)核是不是收到了RST
。當(dāng)本端收到遠(yuǎn)端發(fā)來(lái)的RST
后,內(nèi)核已經(jīng)認(rèn)為此鏈接已經(jīng)關(guān)閉。此時(shí)如果本端應(yīng)用層嘗試去執(zhí)行 讀數(shù)據(jù)操作,比如recv
,應(yīng)用層就會(huì)收到 Connection reset by peer 的報(bào)錯(cuò),意思是遠(yuǎn)端已經(jīng)關(guān)閉連接。如果本端應(yīng)用層嘗試去執(zhí)行寫數(shù)據(jù)操作,比如send
,那么應(yīng)用層就會(huì)收到 Broken pipe 的報(bào)錯(cuò),意思是發(fā)送通道已經(jīng)壞了。這兩個(gè)是開發(fā)過程中很經(jīng)常遇到的報(bào)錯(cuò),感覺大家可以把這篇文章放進(jìn)收藏夾吃灰了,等遇到這個(gè)問題了,再打開來(lái)擦擦灰,說(shuō)不定對(duì)你會(huì)有幫助。出現(xiàn)RST的場(chǎng)景有哪些
RST一般出現(xiàn)于異常情況,歸類為 對(duì)端的端口不可用 和 socket提前關(guān)閉。端口不可用
端口不可用分為兩種情況。要么是這個(gè)端口從來(lái)就沒有"可用"過,比如根本就沒監(jiān)聽(listen)過;要么就是曾經(jīng)"可用",但現(xiàn)在"不可用"了,比如服務(wù)突然崩了。端口未監(jiān)聽
服務(wù)端listen
方法會(huì)創(chuàng)建一個(gè)sock
放入到全局的哈希表
中。此時(shí)客戶端發(fā)起一個(gè)connect
請(qǐng)求到服務(wù)端。服務(wù)端在收到數(shù)據(jù)包之后,第一時(shí)間會(huì)根據(jù)IP和端口從哈希表里去獲取sock
。如果服務(wù)端執(zhí)行過listen
,就能從全局哈希表
里拿到sock
。但如果服務(wù)端沒有執(zhí)行過listen
,那哈希表
里也就不會(huì)有對(duì)應(yīng)的sock
,結(jié)果當(dāng)然是拿不到。此時(shí),正常情況下服務(wù)端會(huì)發(fā)RST
給客戶端。端口未監(jiān)聽就一定會(huì)發(fā)RST嗎?
不一定。上面提到,發(fā)RST的前提是正常情況下,我們看下源碼。//?net/ipv4/tcp_ipv4.c??
//?代碼經(jīng)過刪減
int?tcp_v4_rcv(struct?sk_buff?*skb)
{
????//?根據(jù)ip、端口等信息?獲取sock。
????sk?=?__inet_lookup_skb(