TCP新手誤區–心跳的意義
TCP新手誤區–心跳的意義
背景
最近面試了很多的學生,發現很多TCP的新手對於TCP的使用有一些誤區,而這些坑也是當初我曾經疑惑過得地方。網上很少有文章對這些問題有過詳細的解析,即是有也只是直接給出結論和做法,沒有人將其中的來龍去脈講解清楚,所以我將這些問題的來龍去脈在這一系列的文章中講述出來,希望能讓廣大TCP的新手避開這些坑。
問題
我面試時經常會問的一個問題是當TCP兩端A、B建立了連接後,如果一端拔掉網線或者拔掉電源,那麽另一端能夠收到通知嗎?
答案是不會,但是只有少數人能夠正確的回答這個問題。
原因
TCP是一種有連接的協議,但是這個連接並不是指有一條實際的電路,而是一種虛擬的電路。TCP的建立連接和斷開連接都是通過發送數據實現的,也就是我們常說的三次握手、四次揮手。TCP兩端保存了一種數據的狀態,就代表這種連接,TCP兩端之間的路由設備只是將數據轉發到目的地,並不知道這些數據實際代表了什麽含義,也並沒有在其中保存任何的狀態信息,也就是說中間的路由設備沒有什麽連接的概念,只是將數據轉發到目的地,只有數據的發送者和接受者兩端真正的知道傳輸的數據代表著一條連接。
但是這就說明了一點,如果不發送數據那麽是無法斷開連接的。正常情況下當TCP的一端A調用了SOCKET
的close或者進程結束,操作系統就會按照TCP協議發送FIN
數據報文。B端收到後就會斷開連接。但是當出現了上文所說的異常情況時:被拔掉網線或者斷掉電源,總結起來就是沒有機會發出斷開的FIN
數據報文。那麽和A直連的路由設備雖然知道A設備已經斷開了,但是路由設備並沒有保存連接的狀態信息,所以路由設備也就不可能去通知B端A端的斷開。而B端沒有收到斷開的數據報文就會依然保持連接。所以A端拔掉網線或者斷掉電源後B端是沒辦法收到斷開連接的通知的。
解決方案
保持連接並不是毫無代價的,如果這種異常斷開的連接有很多,那麽勢必會耗費大量的資源,必須要想辦法檢測出這種異常連接。
檢測的方法很簡單,只要讓B端主動通過這個連接向A端繼續發送數據即可。上文說過,A端異常斷開後,和A端直接連接的路由器是知道的。當B端發送的數據經過轉發後到達這個路由器後,必然最終會返回B端一個目的不可達。此時B端立刻就會知道這條連接其實已經異常斷開了。
但是B端不可能知道什麽時候會出現這種異常,所以B端必須定時發送數據來檢測連接是否異常斷開
KEEP_ALIVE
TCP協議本身就提供了一種這樣的機制來探測對端的存活。TCP協議有一個KEEP_LIVE開關,只要打開這個開關就會定時發送一些數據長度為零的探測心跳包,發送的頻率和次數都可以設置,具體的方法在網上搜索tcp keepalive
即可,網上有很多文章,這裏不再贅述。
應用層心跳
除了使用TCP協議本身的保活開關機制,還可以在應用層主動發送心跳數據包,那麽在應用層主動發送心跳數據包的方式和TCP協議本身的保活機制有什麽區別呢?
- 應用層的心跳數據包會耗費更多的帶寬,因為TCP協議的保活機制發送的是數據長度為零心跳包,而應用層的心跳數據包長度則必然會大於0。
- 應用層的心跳數據包可以帶一些應用所需要的數據,隨應用自己控制,而TCP協議的保活機制則是對於應用層透明的,無法利用心跳攜帶數據。
雙向心跳
那麽是否只是一端向另一端發送心跳就行了呢?顯然不行。因為兩端都有可能發生異常斷開的情況。所以TCP連接的兩端必須都向對端發送心跳。
總結
TCP中不使用心跳通常來說並沒有什麽問題,但是一旦遇到了連接異常斷開,那麽就會出現問題。所以任何一個完善的TCP應用都應該使用心跳。
心跳的意義對於很多TCP的初學者而言是個大坑,我寫這篇文章希望初學者能夠在編寫TCP程序時避免這個坑,同時也希望面試者能夠深入理解TCP的心跳機制,能夠取得更好的面試結果。
TCP新手誤區–心跳的意義