三十天學不會TCP,UDP/IP網絡編程 - RST的用法


不知不覺也寫了這么多了,繼續我的自己的推廣大業~完整版可以去gitbook(https://rogerzhu.gitbooks.io/-tcp-udp-ip/content/)看到。

如果對和程序員有關的計算機網絡知識,和對計算機網絡方面的編程有興趣,雖然說現在這種“看不見”的東西真正能在實用中遇到的機會不多,但是我始終覺得無論計算機的語言,熱點方向怎么變化,作為一個程序員,很多基本的知識都應該有所了解。而當時在網上搜索資料的時候,這方面的資料真的是少的可憐,所以,我有幸前兩年接觸了這方面的知識,我覺得我應該把我知道的記錄下來,雖然寫的不一定很好,但是希望能給需要幫助的人多個參考。我的計划是用半年時間來寫完這一系列文章,這個標題也是我對太多速成文章的一種態度,好了,廢話不再多扯了,下面是其中的一節內容,更多內容可以去gitbook上找到。

TCP的六個標識符里,有一個代表RST,reset,但是翻譯成重置連接似乎也不太妥當,報告錯誤也不太合適,作為一個優雅而又邏輯合理的協議,RST對於TCP中出現的錯誤而導致兩端都進入錯誤處理有重要的作用。

情況沒有按照預想的進行

有句詩叫做“不如意事常八九,能與人言無二三”,作為一個龐大,復雜,系統的協議,TCP通信中經常都會出現不是一帆風順的情況。比如前面提過,在三次握手的第二階段,如果在幾次重發都失敗的情況下,會回復一個RST包。正是TCP有這個設計才能讓TCP稱得上連失敗了都處理的很優雅。

為什么要有這么一個RST包而不是在結束的時候直接粗暴的結束或者說啥也不發生的結束呢?還是先讓我們看看現實中打電話的例子,現實中總有那種媽媽給自己的兒子女兒打電話,兒子女兒不耐煩的,甚至產生沖突。而這個時候如果兒女這一端簡單的把電話放下,媽媽那一端就只能聽到環境聲,對於這邊的情況完全不了解。先不說這種對於對端極不禮貌的做法,至少這樣讓對端感到很擔心,不知道那一邊是怎么情況。而即使你再生氣但是你說,不說了,不說了,我掛了,然后掛上電話,哪怕是很重的摔下電話。至少對端可以從嘟嘟聲中知道對端已經掛了電話,不用擔心前面描述的那種擔心了。

TCP也是一樣,如果沒有這個RST包,那么出現出現異常的對端只能等待,因為他根本可能知道對端怎么了。在資源極端重要的網絡環境里,這樣的做法不僅浪費資源更重要的是讓TCP這種號稱核彈都炸不毀的協議陷入了一種混沌的狀態,就像C/C++中的野指針一樣,無法管理無序而又混亂。RST包幾乎涵蓋在TCP每個狀態中,下面在介紹什么時候會發送RST包之前,先得了解一下正常的狀態下的TCP各種狀態變遷,這有了解了正常才能更好理解異常嘛。

TCP狀態變遷圖

我一直認為在計算機領域,有兩個狀態機圖最經典,一個是進程調度時候的進程狀態變遷圖,第二個就是TCP狀態變遷圖了。這個圖清晰的表示了在使用TCP協議通信時,從建立連接到釋放連接的每個過程,如果你能不靠任何外力的情況下畫出這張圖,那么不得不說你已經對TCP的每個階段了如執掌了,如果再理解下TCP中的流量控制等等,那么其實關於這方面的面試你完全不用擔心了。

廢話不多說了,我從網上挑了一張我覺得我看着最舒服的版本:

 

這上面的所有狀態在前面兩節都說過了,除了Established狀態,這個狀態表示TCP處於連接傳輸數據的狀態,算是一個比較穩定的狀態。相比於建立連接和終止連接,這個狀態在狀態本身上並沒有什么特別的,就像一條路修完之后,車子就上去跑就行了。但是在連接狀態下,TCP的太多設計哲學就蘊含在這里面了,我們現實中的道路管理要能借鑒里面的一些思想,很多擁堵情況都能避免了。

 

言歸正傳,在這個圖中,狀態很多,為了演示下怎么樣去看這種狀態機狀態圖,我就敘述下怎么樣從這個圖中找到三次握手中的種種狀態變遷。

狀態機圖主要有兩個部分組成,框框表示某種狀態,框框之間用線條連接,標識狀態的變遷,而在這些線條之上往往會寫着一些說明,這些說明被譽為激勵條件,一個狀態只有經過了某種條件的刺激或者激勵才能變遷為另一個狀態。打個比方吧,比如你在發呆,你處於空閑狀態,這個時候我打你一下,給你一個刺激,你就會從空閑狀態變遷為疼痛狀態了。

所有的狀態都從Closed開始,在這個圖的敘述中,有兩條說明,實現表示客戶,虛線表示服務器,准確的來說,這種說法並不嚴謹,因為在TCP的通信過程中其實沒有服務器和客戶之分,兩邊都在同等的發送數據。而在這個圖或者一般的表述中,主動發起連接的那一方一般會被稱之為客戶端,被動接受的那一段是服務器端。在了解了這些先決條件之后,那么就來一起找找這個圖中的三次握手在哪里吧。

首先看Closed狀態中的實線,也就是表示客戶的狀態,在右邊,主動發送SYN之后,進入SYN_SENT狀態,三次握手第一步完成。而在這之后,收到SYN和ACK包,然后發送ACK之后客戶這邊就進入了連接狀態。

然后再看看另一條路,從Closed狀態虛線進入listen狀態,這里不需要任何激勵條件就能完成,因為默認服務器一直在監聽客戶發來的請求,不然那你這個圖就畫不下去了。在收到SYN,發送SYN和ACK之后進入SYN_RCVD狀態,也就是三次握手的第二步。然后就是繼續收到對端的ACK之后,進入連接狀態。

每個狀態機圖都能很方便的變成很多獨立的小單元,這也是一個狀態機圖繪畫的好與不好的一個標志。好的狀態機圖狀態之間變遷關系清晰,彼此耦合性很小。而差的你就感覺在這種狀態變化的指示下只會變成一團混亂。

仔細看一下這個狀態機圖,你會發現很多前面並沒有提及的有趣的狀態,他們中很多都是很邊緣的情況,但是作為一個嚴謹的協議設計者,應該最大可能留下最小的漏洞。比如圖中你會發現從listen狀態可以直接變成SYN_SEND狀態,回想一下前面說了服務器端會默認為是listen狀態從而可以監聽對端來的請求。但是如果這個時候服務器端主動發送了SYN,那么他其實就是我們定義中的“客戶”了,所以圖中用實線標識這種情況下會SYN_SEND狀態。

另外一個在發起連接階段的時候的特殊情況就是SYN_SEND和SYN_RCVD中間那條線,如果一個客戶在進入SYN_SEND同時收到對端發送回來的SYN請求,通俗點說就是兩端同時發起了連接,兩邊都是客戶了,這個時候TCP不會就此終止,而是進入SYN_RCVD,然后再經歷后面的過程之后,兩邊都進入了Established狀態。

這就是這張圖的上半部分,如果有興趣可以看看下半部分整個的關閉流程作為聯系。

RST狀態變遷圖

在了解了正常狀態變遷之后,異常狀態就不會顯得特別的亂了,在網上我找到了這么一張圖,我覺得畫的特別好,就拿來借用了。

 

 

對比上面的圖,這張圖用紅色的部分標識了發送RST的情況,但是也簡化了一些上面有的狀態變遷,不過這對於了解整個TCP過程中RST的狀態變遷沒有那么重要了。

從這個圖中第一個可以學到的知識就是經歷所有RST狀態之后,連接都會進入Closed狀態,也就是結束。RST的狀態基本分成兩種,超時和包丟失。

首先看看超時的情況,在圖中就三種包重傳超時,SYN,FIN和數據包,可以看到超時都是某種“主動的動作”。比如在三次握手的第二個階段,重傳SYN+ACK一直超時,服務器端就只能發送一個RST到對端了。同理在FIN超時和本身傳輸數據超時之后,RST也會發送到對方。

第二類是丟失,其實本質上上和上一種情況是一直的,在ACK丟失的情況下,在對端看來就是發送包超時,所以在這種情況下主要是接收RST。

那么在現實生活中,什么樣的情況會導致超時呢?其實很多情況都可能發生,比如某一端程序突然崩潰,某一端突然沒電了等等,這種情況下都會引起超時,從而會導致RST包的發送。如果用官方的語言總結下,RST包會在以下三種情況下被發送:

到達的端口不存在,比如說在客戶端發送SYNC想建立連接的時候,對端並沒有任何監聽端口,那么就會發生超時,從而發送RST包。而在UDP這種沒有連接的協議中,一個ICMP端口不可達消息就能解決這個問題(如果你對這些名詞很陌生,可以看看前面的文章了)。

連接被異常終止, 比如說應用程序崩潰了。

檢測半關閉連接,這個半關閉的概念其實在上一篇的時候有所提及,但是不詳細,這里正好說明一下。半關閉連接主要發生在某一段已經關閉或者異常終止但是另外一段並不知道的情況下。比如在通信的過程中,突然拔掉服務器那邊的網線,然后再重啟服務器和其應用程序。這個時候連接就處於一個半關閉狀態,此時客戶機再向對方發一個消息,服務器端會回復一個RST消息,本端就知道剛才發生了什么。

說了這么多,言而總之其實RST主要作用有兩個,報告本端異常,告知對方錯誤,記住這兩句話就差不多記住了RST包的作用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM