徹底搞定:手繪TCP狀態機
- 2019 年 10 月 7 日
- 筆記
知識卡

來源:https://github.com/wangcy6/weekly 每日一題 第二題
情景對話
老王:小王,最近工作注意力不集中呀!
小王:我在等面試結果呢!
老王:你感覺如何呢?
小王:
當時情況是這樣的!
大王:你擅長window,還是liunx?
小王:Linux(這年頭誰還在寫window程序)
大王;那你對網絡編程一定很熟悉 吧?
小王:那是當然(都是小菜一碟)。
大王:請繪製TCP狀態轉換過程?
小王:。。。。(這個誰能記住他,絞盡腦汁想,5分鐘過去了)
大王:還有什麼要補充的嗎?(耐心等待)
小王:不會寫,有幾個記不清楚(5分鐘過去了)
大王:好,回去等通知。
老王:我來講一講,需要解決下面幾個問題

自我提問
問題1 socket通訊過程,和抓包格式 時間限時在2分鐘
小王:
socket常用接口 accept,read,write close,我經常用很熟悉呀,沒什麼可學的了, 還有tcp協議那個圖 我看多少遍?
(老王)我這裡提示一下,不做深入討論,時間限時在2分鐘。

完整通訊過程


用戶態
問題2 :三次握手和四次揮手過程 ,時間限時在5 分鐘

客戶端和服務器同時發現異常,都進行關閉這個連接

我沒遇到過
問題3 在機器重啟,服務重啟,網絡斷開等情況下呢?
小王:
遇到這個情況,不是epoll, SO_KEEPALIVE read返回 0代表接受。一般都是這麼處理的
閑話少說,從四次揮手最有一步異常說起。
老王:RichardStevens說過這樣2句話
There are two reasons for the TIME_WAIT state:
一、保證TCP協議的全雙工連接能夠可靠關閉
To implement TCP's full-duplex connection termination reliably
二、保證這次連接的重複數據段從網絡中消失
To allow old duplicate segments to expire in the network
小王:
表示不理解,上面不是同一個意思嗎,如果達到不了就消失?
(老王)錯誤,繼續看
根據第三版《UNIX網絡編程 卷1》2.7節,TIME_WAIT狀態的主要目的有兩個:
- 優雅的關閉TCP連接,也就是盡量保證被動關閉的一端收到它自己發出去的FIN報文的ACK確認報文;
- 處理延遲的重複報文,這主要是為了避免前後兩個使用相同四元組的連接中的前一個連接的報文干擾後一個連接。 保證TCP協議的全雙工連接能夠可靠關閉:(解釋)
ACK is lost. The server will resend its final FIN, so the client must maintain state information, allowing it to resend the final ACK. If it did not maintain this information, it would respond with an RST (a different type of TCP segment), which would be interpreted by the server as an error
發生條件:
服務正常,網絡正常。
- 服務正常,網絡正常:
B發送FIN,進入LAST_ACK狀態,A收到這個FIN包後發送ACK包,B收到這個ACK包,然後進入CLOSED狀態
- 服務正常,網絡擁塞,網絡連接良好
B發送FIN,進入LAST_ACK狀態,A收到這個FIN包後發送ACK包,由於某種原因,這個ACK包丟失了,B沒有收到ACK包,然後B等待ACK包超時,又向A發送了一個FIN包 a) 假如這個時候,A還是處於TIME_WAIT狀態(也就是TIME_WAIT持續的時間在2MSL內)A收到這個FIN包後向B發送了一個ACK包,B收到這個ACK包進入CLOSED狀態
b) 假如這個時候,A已經從TIME_WAIT狀態變成了CLOSED狀態 A收到這個FIN包後,認為這是一個錯誤的連接,向B發送一個RST包,當B收到這個RST包,進入CLOSED狀態
- 服務不正常 或者網絡斷開 c) 假如這個時候,A掛了(假如這台機器炸掉了)【第四種情況,不在參考鏈接里】 B沒有收到A的回應,那麼會繼續發送FIN包,也就是觸發了TCP的重傳機制,如果A還是沒有回應,B還會繼續 發送FIN包,直到重傳超時(至於這個時間是多長需要仔細研究),B重置這個連接,進入CLOSED狀態,
小王:原來是這樣
畫外音
網絡斷了,節點重啟了,是無法處理的。只能依靠Rst解決。
下面情況如果ack,不能按時到達,阻止建立新的連接。
小王:原來是這樣
畫外音:
TCP連接中的一端發送了FIN報文之後如果收不到對端針對該FIN的ACK,則會反覆多次重傳FIN報文. 處於TIME_WAIT狀態的一端在收到重傳的FIN時會重新計時(rfc793 以及 linux kernel源代碼tcp_timewait_state_process函數
保證這次連接的重複數據段從網絡中消失(解釋)
發生條件:
Note that it is very unlikely that delayed segments will cause problems like this.
Firstly the address and port of each end point needs to be the same; which is normally unlikely as the client's port is usually selected for you by the operating system from the ephemeral port range and thus changes between connections.
Secondly, the sequence numbers for the delayed segments need to be valid in the new connection which is also unlikely. However, should both of these things occur then TIME_WAIT
will prevent the new connection's data from being corrupted.
畫外音:
必須原來的ip,原來的端口發起的連接,想想一個服務器連接多個客戶端,四元組 是唯一的。

*Due to a shortened TIME-WAIT state, a delayed TCP segment has been accepted in an unrelated connection.*

image.png
小王:原來是這樣!
畫外音:
四次揮手已經完成,最有一個ack順利達到對方,一方進入closed狀態(假如3秒內完成)
對方依然要等待2MSL(剩餘28秒),這個等待不是多餘等待,而是防止
這個時候雙方如果馬上同時closed(是允許建立新的連接。這是正常通訊過程)、
還有延遲重發的數據包。對同一個pair連接,新老數據造成混亂。
tcp協議提到內核接受數據是根據port區分是那個,而不是fd。
小王偷偷寫這麼幾句話
time_wait 存在的意義有2點
(1) TCP 可靠傳輸,保證四次揮手最後一個ack 順利到達對方。
採用方式是:如果獲取到對方重新發送fin請求,需要重新計時間,維持TIme_wait狀態。
保障每次發送出去ack都最終結果(收到或者消失)
如果在網絡出斷網,或者服務節點重啟,或者對方不啟tcp重傳機制上面方法是無法處理的
應該超時或者返回Rst包出路 結束last_ack狀態。
(2 ) TCP基於四元組建立連接, 假如客戶端端口 不隨機產生,而是相同ip,相同的
端口,再次連接的話。可能出現,雖然old 連接已經消失,但是在網絡中數據可能存在。
以tcp 內核中斷處理 網絡消息是根據 端口劃分的。會造成新舊數據混亂。
TCP不能給處於TIME_WAIT狀態的連接啟動新的連接。 TIME_WAIT的持續時間是2MSL,保證在建立新的連接之前老的重複分組在網絡中消逝。 這個規則有一個例外:如果到達的SYN的序列號大於前一個連接的結束序列號, 源自Berkeley的實現將給當前處於TIME_WAIT狀態的連接啟動新的化身。 do_time_wait

tcp狀態機
參考
- TCP那些事(上)
- TIME_WAIT狀態下對接收到的數據包如何處理
- UNIX Network Programming
- 趣談 操作系統
- TIME_WAIT and its design implications for protocols and scalable client server systems