上周有位讀者找我說,他在面試騰訊的時候,遇到了這么個問題:

這個屬于 TCP 例外斷開連接的場景,這部分內容在我的「圖解網路」還沒有詳細介紹過,這次就乘著這次機會補一補,

小林在 csdn 寫了很多圖解網路文章,共輸出了 15萬字 和畫了 500 張圖,有csdn的朋友建議我把圖解網路文章整理成電子檔,方便讀者閱讀,
那必須的呀,所以小林花了一段時間整理圖解網路電子檔案,效果如題:

大家可以作為面試突擊的手冊,現在開源給大家下載:點擊下載圖解網路 PDF
好了,繼續今天的主題,
這個問題有幾個關鍵詞:
- 沒有開啟 keepalive;
- 一直沒有資料互動;
- 行程崩潰;
- 主機崩潰;
我們先來認識認識什么是 TCP keepalive 呢?
這東西其實就是 TCP 的保活機制,它的作業原理我之前的文章寫過,這里就直接貼下以前的內容,

如果兩端的 TCP 連接一直沒有資料互動,達到了觸發 TCP 保活機制的條件,那么內核里的 TCP 協議堆疊就會發送探測報文,
- 如果對端程式是正常作業的,當 TCP 保活的探測報文發送給對端, 對端會正常回應,這樣 TCP 保活時間會被重置,等待下一個 TCP 保活時間的到來,
- 如果對端主機崩潰,或對端由于其他原因導致報文不可達,當 TCP 保活的探測報文發送給對端后,石沉大海,沒有回應,連續幾次,達到保活探測次數后,TCP 會報告該 TCP 連接已經死亡,
所以,TCP 保活機制可以在雙方沒有資料互動的情況,通過探測報文,來確定對方的 TCP 連接是否存活,

注意,應用程式若想使用 TCP 保活機制需要通過 socket 介面設定 SO_KEEPALIVE 選項才能夠生效,如果沒有設定,那么就無法使用 TCP 保活機制,
知道了 TCP keepalive 作用,我們再回過頭看題目中的「主機崩潰」這種情況,
在沒有開啟 TCP keepalive,且雙方一直沒有資料互動的情況下,如果客戶端的「主機崩潰」了,會發生什么,
客戶端主機崩潰了,服務端是無法感知到的,在加上服務端沒有開啟 TCP keepalive,又沒有資料互動的情況下,服務端的 TCP 連接將會一直處于 ESTABLISHED 連接狀態,直到服務端重啟行程,
所以,我們可以得知一個點,在沒有使用 TCP 保活機制且雙方不傳輸資料的情況下,一方的 TCP 連接處在 ESTABLISHED 狀態,并不代表另一方的連接還一定正常,
那題目中的「行程崩潰」的情況呢?
我自己做了實驗,使用 kill -9 來模擬行程崩潰的情況,發現在 kill 掉行程后,服務端會發送 FIN 報文,與客戶端進行四次揮手,
所以,即使沒有開啟 TCP keepalive,且雙方也沒有資料互動的情況下,如果其中一方的行程發生了崩潰,這個程序作業系統是可以感知的到的,于是就會發送 FIN 報文給對方,然后與對方進行 TCP 四次揮手,

以上就是對這個面試題的回答,接下來我們看看在「有資料傳輸」的場景下的一些例外情況:
- 第一種,客戶端主機宕機,又迅速重啟,會發生什么?
- 第二種,客戶端主機宕機,一直沒有重啟,會發生什么?
客戶端主機宕機,又迅速重啟
在客戶端主機宕機后,服務端向客戶端發送的報文會得不到任何的回應,在一定時長后,服務端就會觸發超時重傳機制,重傳未得到回應的報文,
服務端重傳報文的程序中,客戶端主機重啟完成后,客戶端的內核就會接收重傳的報文,然后根據報文的資訊傳遞給對應的行程:
- 如果客戶端主機上沒有行程監聽該 TCP 報文的目標埠號,那么客戶端內核就會**回復 RST 報文,重置該 TCP 連接*;
- 如果客戶端主機上有行程監聽該 TCP 報文的目標埠號,由于客戶端主機重啟后,之前的 TCP 連接的資料結構已經丟失了,客戶端內核里協議堆疊會發現找不到該 TCP 連接的 socket 結構體,于是就會回復 RST 報文,重置該 TCP 連接,
所以,只要有一方重啟完成后,收到之前 TCP 連接的報文,都會回復 RST 報文,以斷開連接,
客戶端主機宕機,一直沒有重啟
這種情況,服務端超時重傳報文的次數達到一定閾值后,內核就會判定出該 TCP 有問題,然后通過 Socket 介面告訴應用程式該 TCP 連接出問題了,一般就是 ETIMEOUT 狀態碼,
那具體重傳幾次呢?
在 Linux 系統中,提供一個叫 tcp_retries2 配置項,默認值是 15,如下圖:

這個內核引數是控制,在 TCP 連接建立的情況下,超時重傳的最大次數,
不過 tcp_retries2 設定了 15 次,并不代表 TCP 超時重傳了 15 次才會通知應用程式終止該 TCP 連接,內核還會基于「最大超時時間」來判定,
每一輪的超時時間都是倍數增長的,比如第一次觸發超時重傳是在 2s 后,第二次則是在 4s 后,第三次則是 8s 后,以此類推,

內核會根據 tcp_retries2 設定的值,計算出一個最大超時時間,

在重傳報文且一直沒有收到對方回應的情況時,先達到「最大重傳次數」或者「最大超時時間」這兩個的其中一個條件后,就會停止重傳,
最后說句,TCP 牛逼,啥例外都考慮到了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/287777.html
標籤:其他
