??大家好,我是小林。昨晚有位讀者問了我這么個(gè)問題:大概意思是,一個(gè)已經(jīng)建立的 TCP 連接,客戶端中途宕機(jī)了,而服務(wù)端此時(shí)也沒有數(shù)據(jù)要發(fā)送,一直處于 establish 狀態(tài),客戶端恢復(fù)后,向服務(wù)端建立連接,此時(shí)服務(wù)端會怎么處理?看過我的圖解網(wǎng)絡(luò)的讀者都知道,TCP 連接是由「四元組」唯一確認(rèn)的。然后這個(gè)場景中,客戶端的IP、服務(wù)端IP、目的端口并沒有變化,所以這個(gè)問題關(guān)鍵要看客戶端發(fā)送的 SYN 報(bào)文中的源端口是否和上一次連接的源端口相同。1. 客戶端的 SYN 報(bào)文里的端口號與歷史連接不相同如果客戶端恢復(fù)后發(fā)送的 SYN 報(bào)文中的源端口號跟上一次連接的源端口號不一樣,此時(shí)服務(wù)端會認(rèn)為是新的連接要建立,于是就會通過三次握手來建立新的連接。那舊連接里處于 establish 狀態(tài)的服務(wù)端最后會怎么樣呢?
2. 客戶端的 SYN 報(bào)文里的端口號與歷史連接相同如果客戶端恢復(fù)后,發(fā)送的 SYN 報(bào)文中的源端口號跟上一次連接的源端口號一樣,也就是處于 establish 狀態(tài)的服務(wù)端收到了這個(gè) SYN 報(bào)文。大家覺得服務(wù)端此時(shí)會做什么處理呢?
丟掉 SYN 報(bào)文?
回復(fù) RST 報(bào)文?
回復(fù) ACK 報(bào)文?
剛開始我看到這個(gè)問題的時(shí)候,也是沒有思路的,因?yàn)橹皼]關(guān)注過,然后這個(gè)問題不能靠猜,所以我就看了 RFC 規(guī)范和看了 Linux 內(nèi)核源碼,最終知道了答案。我不賣關(guān)子,先直接說答案。處于 establish 狀態(tài)的服務(wù)端如果收到了客戶端的 SYN 報(bào)文(注意此時(shí)的 SYN 報(bào)文其實(shí)是亂序的,因?yàn)?SYN 報(bào)文的初始化序列號其實(shí)是一個(gè)隨機(jī)數(shù)),會回復(fù)一個(gè)攜帶了正確序列號和確認(rèn)號的 ACK 報(bào)文,這個(gè) ACK 被稱之為 Challenge ACK。接著,客戶端收到這個(gè) Challenge ACK,發(fā)現(xiàn)序列號并不是自己期望收到的,于是就會回 RST 報(bào)文,服務(wù)端收到后,就會釋放掉該連接。
RFC 文檔解釋
rfc793 文檔里的第 34 頁里,有說到這個(gè)例子。原文的解釋我也貼出來給大家看看。
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 報(bào)文時(shí),內(nèi)核會調(diào)用這些函數(shù):tcp_v4_rcv ??->?tcp_v4_do_rcv ????->?tcp_rcv_established ??????->?tcp_validate_incoming ????????->?tcp_send_ack 我們只關(guān)注 tcp_validate_incoming 函數(shù)是怎么處理 SYN 報(bào)文的,精簡后的代碼如下:從上面的代碼實(shí)現(xiàn)可以看到,處于 establish 狀態(tài)的服務(wù)端,在收到報(bào)文后,首先會判斷序列號是否在窗口內(nèi),如果不在,則看看 RST 標(biāo)記有沒有被設(shè)置,如果有就會丟掉。然后如果沒有 RST 標(biāo)志,就會判斷是否有 SYN 標(biāo)記,如果有 SYN 標(biāo)記就會跳轉(zhuǎn)到 syn_challenge 標(biāo)簽,然后執(zhí)行 tcp_send_challenge_ack 函數(shù)。tcp_send_challenge_ack 函數(shù)里就會調(diào)用 tcp_send_ack 函數(shù)來回復(fù)一個(gè)攜帶了正確序列號和確認(rèn)號的 ACK 報(bào)文。