最近有位讀者問了我這么個問題:大概意思是,一個已經(jīng)建立的 TCP 連接,客戶端中途宕機了,而服務(wù)端此時也沒有數(shù)據(jù)要發(fā)送,一直處于 establish 狀態(tài),客戶端恢復(fù)后,向服務(wù)端建立連接,此時服務(wù)端會怎么處理?看過我的圖解網(wǎng)絡(luò)的讀者都知道,TCP 連接是由「四元組」唯一確認(rèn)的。然后這個場景中,客戶端的IP、服務(wù)端IP、目的端口并沒有變化,所以這個問題關(guān)鍵要看客戶端發(fā)送的 SYN 報文中的源端口是否和上一次連接的源端口相同。1. 客戶端的 SYN 報文里的端口號與歷史連接不相同如果客戶端恢復(fù)后發(fā)送的 SYN 報文中的源端口號跟上一次連接的源端口號不一樣,此時服務(wù)端會認(rèn)為是新的連接要建立,于是就會通過三次握手來建立新的連接。那舊連接里處于 establish 狀態(tài)的服務(wù)端最后會怎么樣呢?
2. 客戶端的 SYN 報文里的端口號與歷史連接相同如果客戶端恢復(fù)后,發(fā)送的 SYN 報文中的源端口號跟上一次連接的源端口號一樣,也就是處于 establish 狀態(tài)的服務(wù)端收到了這個 SYN 報文。大家覺得服務(wù)端此時會做什么處理呢?
丟掉 SYN 報文?
回復(fù) RST 報文?
回復(fù) ACK 報文?
剛開始我看到這個問題的時候,也是沒有思路的,因為之前沒關(guān)注過,然后這個問題不能靠猜,所以我就看了 RFC 規(guī)范和看了 Linux 內(nèi)核源碼,最終知道了答案。我不賣關(guān)子,先直接說答案。處于 establish 狀態(tài)的服務(wù)端如果收到了客戶端的 SYN 報文(注意此時的 SYN 報文其實是亂序的,因為 SYN 報文的初始化序列號其實是一個隨機數(shù)),會回復(fù)一個攜帶了正確序列號和確認(rèn)號的 ACK 報文,這個 ACK 被稱之為 Challenge ACK。接著,客戶端收到這個 Challenge ACK,發(fā)現(xiàn)序列號并不是自己期望收到的,于是就會回 RST 報文,服務(wù)端收到后,就會釋放掉該連接。
RFC 文檔解釋
rfc793 文檔里的第 34 頁里,有說到這個例子。原文的解釋我也貼出來給大家看看。
When the SYN arrives at line 3, TCP B, being in a synchronized state, and the incoming segment outside the window, responds with an acknowledgment indicating what sequence it next expects to hear (ACK 100).
TCP A sees that this segment does not acknowledge anything it sent and, being unsynchronized, sends a reset (RST) because it has detected a half-open connection.
TCP B aborts at line 5.
TCP A willcontinue to try to establish the connection;
我就不瞎翻譯了,意思和我在前面用中文說的解釋差不多。
源碼分析
處于 establish 狀態(tài)的服務(wù)端如果收到了客戶端的 SYN 報文時,內(nèi)核會調(diào)用這些函數(shù):