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端繼續傳送資料即可
但是B端不可能知道什麼時候會出現這種異常,所以B端必須定時傳送資料來檢測連線是否異常斷開。資料的內容無關緊要,任何資料都能達到這個效果。這個資料就是我們經常在TCP程式設計中所說的心跳。
KEEP_ALIVE
TCP協議本身就提供了一種這樣的機制來探測對端的存活。TCP協議有一個KEEP_LIVE開關,只要開啟這個開關就會定時傳送一些資料長度為零的探測心跳包,傳送的頻率和次數都可以設定,具體的方法在網上搜索tcp keepalive
應用層心跳
除了使用TCP協議本身的保活開關機制,還可以在應用層主動傳送心跳資料包,那麼在應用層主動傳送心跳資料包的方式和TCP協議本身的保活機制有什麼區別呢?
- 應用層的心跳資料包會耗費更多的頻寬,因為TCP協議的保活機制傳送的是資料長度為零心跳包,而應用層的心跳資料包長度則必然會大於0。
- 應用層的心跳資料包可以帶一些應用所需要的資料,隨應用自己控制,而TCP協議的保活機制則是對於應用層透明的,無法利用心跳攜帶資料。
雙向心跳
那麼是否只是一端向另一端傳送心跳就行了呢?顯然不行。因為兩端都有可能發生異常斷開的情況。所以TCP連線的兩端必須都向對端傳送心跳。
總結
TCP中不使用心跳通常來說並沒有什麼問題,但是一旦遇到了連線異常斷開,那麼就會出現問題。所以任何一個完善的TCP應用都應該使用心跳。
心跳的意義對於很多TCP的初學者而言是個大坑,我寫這篇文章希望初學者能夠在編寫TCP程式時避免這個坑,同時也希望面試者能夠深入理解TCP的心跳機制,能夠取得更好的面試結果。