大家好,我是小林。
今天,聊一個有趣的問題:拔掉網線幾秒,再插回去,原本的 TCP 連接還存在嗎?
可能有的同學會說,網線都被拔掉了,那說明物理層被斷開了,那在上層的傳輸層理應也會斷開,所以原本的 TCP 連接就不會存在的了。就好像, 我們撥打有線電話的時候,如果某一方的電話線被拔了,那么本次通話就徹底斷了。
真的是這樣嗎?
上面這個邏輯就有問題。問題在于,錯誤的認為拔掉網線這個動作會影響傳輸層,事實上并不會影響。
實際上,TCP 連接在 Linux 內核中是一個名為 struct socket 的結構體,該結構體的內容包含 TCP 連接的狀態等信息。當拔掉網線的時候,操作系統并不會變更該結構體的任何內容,所以 TCP 連接的狀態也不會發生改變。
我在我的電腦上做了個小實驗,我用 ssh 終端連接了我的云服務器,然后我通過斷開 wifi 的方式來模擬拔掉網線的場景,此時查看 TCP 連接的狀態沒有發生變化,還是處于 ESTABLISHED 狀態。
通過上面這個實驗結果,我們知道了,拔掉網線這個動作并不會影響 TCP 連接的狀態。接下來,要看拔掉網線后,雙方做了什么動作。所以,針對這個問題,要分場景來討論:
拔掉網線后,有數據傳輸;
拔掉網線后,沒有數據傳輸;
拔掉網線后,有數據傳輸
在客戶端拔掉網線后,服務端向客戶端發送的數據報文會得不到任何的響應,在等待一定時長后,服務端就會觸發超時重傳機制,重傳未得到響應的數據報文。如果在服務端重傳報文的過程中,客戶端剛好把網線插回去了,由于拔掉網線并不會改變客戶端的 TCP 連接狀態,并且還是處于 ESTABLISHED 狀態,所以這時客戶端是可以正常接收服務端發來的數據報文的,然后客戶端就會回 ACK 響應報文。此時,客戶端和服務端的 TCP 連接依然存在的,就感覺什么事情都沒有發生。但是,如果如果在服務端重傳報文的過程中,客戶端一直沒有將網線插回去,服務端超時重傳報文的次數達到一定閾值后,內核就會判定出該 TCP 有問題,然后通過 Socket 接口告訴應用程序該 TCP 連接出問題了,于是服務端的 TCP 連接就會斷開。而等客戶端插回網線后,如果客戶端向服務端發送了數據,由于服務端已經沒有與客戶端相同四元祖的 TCP 連接了,因此服務端內核就會回復 RST 報文,客戶端收到后就會釋放該 TCP 連接。此時,客戶端和服務端的 TCP 連接都已經斷開了。
那 TCP 的數據報文具體重傳幾次呢?
在 Linux 系統中,提供了一個叫 tcp_retries2 配置項,默認值是 15,如下圖:
這個內核參數是控制,在 TCP 連接建立的情況下,超時重傳的最大次數。不過 tcp_retries2 設置了 15 次,并不代表 TCP 超時重傳了 15 次才會通知應用程序終止該 TCP 連接,內核還會基于「最大超時時間」來判定。
每一輪的超時時間都是倍數增長的,比如第一次觸發超時重傳是在 2s 后,第二次則是在 4s 后,第三次則是 8s 后,以此類推。
內核會根據 tcp_retries2 設置的值,計算出一個最大超時時間。
在重傳報文且一直沒有收到對方響應的情況時,先達到「最大重傳次數」或者「最大超時時間」這兩個的其中一個條件后,就會停止重傳,然后就會斷開 TCP 連接。
拔掉網線后,沒有數據傳輸
針對拔掉網線后,沒有數據傳輸的場景,還得看是否開啟了 TCP keepalive 機制 (TCP 保活機制)。如果沒有開啟 TCP keepalive 機制,在客戶端拔掉網線后,并且雙方都沒有進行數據傳輸,那么客戶端和服務端的 TCP 連接將會一直保持存在。而如果開啟了 TCP keepalive 機制,在客戶端拔掉網線后,即使雙方都沒有進行數據傳輸,在持續一段時間后,TCP 就會發送探測報文:
如果對端是正常工作的。當 TCP 保活的探測報文發送給對端, 對端會正常響應,這樣 TCP 保活時間會被重置,等待下一個 TCP 保活時間的到來。
如果對端主機崩潰,或對端由于其他原因導致報文不可達。當 TCP 保活的探測報文發送給對端后,石沉大海,沒有響應,連續幾次,達到保活探測次數后,TCP 會報告該 TCP 連接已經死亡。
所以,TCP 保活機制可以在雙方沒有數據交互的情況,通過探測報文,來確定對方的 TCP 連接是否存活。
TCP keepalive 機制具體是怎么樣的?
這個機制的原理是這樣的:定義一個時間段,在這個時間段內,如果沒有任何連接相關的活動,TCP 保活機制會開始作用,每隔一個時間間隔,發送一個探測報文,該探測報文包含的數據非常少,如果連續幾個探測報文都沒有得到響應,則認為當前的 TCP 連接已經死亡,系統內核將錯誤信息通知給上層應用程序。在 Linux 內核可以有對應的參數可以設置保活時間、保活探測的次數、保活探測的時間間隔,以下都為默認值:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
tcp_keepalive_time=7200:表示保活時間是 7200 秒(2小時),也就 2 小時內如果沒有任何連接相關的活動,則會啟動保活機制;
tcp_keepalive_intvl=75:表示每次檢測間隔 75 秒;
tcp_keepalive_probes=9:表示檢測 9 次無響應,認為對方是不可達的,從而中斷本次的連接。
也就是說在 Linux 系統中,最少需要經過 2 小時 11 分 15 秒才可以發現一個「死亡」連接。
圖片注意,應用程序若想使用 TCP 保活機制需要通過 socket 接口設置 SO_KEEPALIVE選項才能夠生效,如果沒有設置,那么就無法使用 TCP 保活機制。
TCP keepalive 機制探測的時間也太長了吧?
對的,是有點長。TCP keepalive 是 TCP 層(內核態) 實現的,它是給所有基于 TCP 傳輸協議的程序一個兜底的方案。實際上,我們應用層可以自己實現一套探測機制,可以在較短的時間內,探測到對方是否存活。比如,web 服務軟件一般都會提供 keepalive_timeout 參數,用來指定 HTTP 長連接的超時時間。如果設置了 HTTP 長連接的超時時間是 60 秒,web 服務軟件就會啟動一個定時器,如果客戶端在完后一個 HTTP 請求后,在 60 秒內都沒有再發起新的請求,定時器的時間一到,就會觸發回調函數來釋放該連接。
總結
客戶端拔掉網線后,并不會直接影響 TCP 連接狀態。所以,拔掉網線后,TCP 連接是否還會存在,關鍵要看拔掉網線之后,有沒有進行數據傳輸。有數據傳輸的情況:
在客戶端拔掉網線后,如果服務端發送了數據報文,那么在服務端重傳次數沒有達到最大值之前,客戶端就插回了網線,那么雙方原本的 TCP 連接還是能正常存在,就好像什么事情都沒有發生。
在客戶端拔掉網線后,如果服務端發送了數據報文,在客戶端插回網線之前,服務端重傳次數達到了最大值時,服務端就會斷開 TCP 連接。等到客戶端插回網線后,向服務端發送了數據,因為服務端已經斷開了與客戶端相同四元組的 TCP 連接,所以就會回 RST 報文,客戶端收到后就會斷開 TCP 連接。至此, 雙方的 TCP 連接都斷開了。
沒有數據傳輸的情況:
如果雙方都沒有開啟 TCP keepalive 機制,那么在客戶端拔掉網線后,如果客戶端一直不插回網線,那么客戶端和服務端的 TCP 連接狀態將會一直保持存在。
如果雙方都開啟了 TCP keepalive 機制,那么在客戶端拔掉網線后,如果客戶端一直不插回網線,TCP keepalive 機制會探測到對方的 TCP 連接沒有存活,于是就會斷開 TCP 連接。而如果在 TCP 探測期間,客戶端插回了網線,那么雙方原本的 TCP 連接還是能正常存在。
除了客戶端拔掉網線的場景,還有客戶端「宕機和殺死進程」的兩種場景。第一個場景,客戶端宕機這件事跟拔掉網線是一樣無法被服務端的感知的,所以如果在沒有數據傳輸,并且沒有開啟 TCP keepalive 機制時,服務端的 TCP 連接將會一直處于 ESTABLISHED 連接狀態,直到服務端重啟進程。所以,我們可以得知一個點。在沒有使用 TCP 保活機制,且雙方不傳輸數據的情況下,一方的 TCP 連接處在 ESTABLISHED 狀態時,并不代表另一方的 TCP 連接還一定是正常的。第二個場景,殺死客戶端的進程后,客戶端的內核就會向服務端發送 FIN 報文,與客戶端進行四次揮手。所以,即使沒有開啟 TCP keepalive,且雙方也沒有數據交互的情況下,如果其中一方的進程發生了崩潰,這個過程操作系統是可以感知的到的,于是就會發送 FIN 報文給對方,然后與對方進行 TCP 四次揮手。
原文標題:拔掉網線后, 原本的 TCP 連接還存在嗎?
文章出處:【微信公眾號:程序人生】歡迎添加關注!文章轉載請注明出處。
-
服務器
+關注
關注
12文章
9308瀏覽量
86071 -
TCP
+關注
關注
8文章
1378瀏覽量
79303 -
WIFI
+關注
關注
81文章
5309瀏覽量
204791
原文標題:拔掉網線后, 原本的 TCP 連接還存在嗎?
文章出處:【微信號:coder_life,微信公眾號:程序人生】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
TCP協議的性能測試與評估方法
TCP協議的安全性分析
TCP協議與UDP協議的區別
Keepalive基礎知識
![<b class='flag-5'>Keepalive</b>基礎知識](https://file1.elecfans.com/web3/M00/02/FC/wKgZPGdjfeyAXRKCAABIjlIRlok458.png)
MODBUS TCP 轉 CANOpen
![MODBUS <b class='flag-5'>TCP</b> 轉 CANOpen](https://file1.elecfans.com/web2/M00/08/97/wKgaombyRnmAZrAyAAGHzRG0j8s875.png)
EtherCAT從站轉Modbus TCP協議網關(YC-ECT-TCP)
![EtherCAT從站轉Modbus <b class='flag-5'>TCP</b>協議網關(YC-ECT-<b class='flag-5'>TCP</b>)](https://file1.elecfans.com//web2/M00/03/DB/wKgaoma-ynaAb1MnAABen-7Qlks759.png)
簡述TCP協議的三次握手機制
Modbus(TCP)轉Profinet從總線協議轉換網關(JM-TCP-PN)
![Modbus(<b class='flag-5'>TCP</b>)轉Profinet從總線協議轉換網關(JM-<b class='flag-5'>TCP</b>-PN)](https://file1.elecfans.com/web2/M00/03/D6/wKgaoma-teKAT0IDAAMUO8RWAYc731.png)
評論