Stories

Detail Return Return

字節二面:TCP 鏈接中,接收方不調用 recv,會出現什麼情況?——拆解大廠面試題(校招) - Stories Detail

當 TCP 連接中的接收方應用程序不調用 recv() (或類似的接收函數,如 read()) 時,會發生一系列由 TCP 協議棧自動處理的情況,核心是 TCP 的流量控制機制會介入。

我們假設有兩個主要部分:發送方 (Sender)接收方 (Receiver)。每一方都有應用程序層 (App) 和內核 TCP 層 (Kernel)。

階段 1: 正常數據傳輸

1、接收方 App 正常調用 recv()

2、接收方 Kernel 的接收緩衝區 (Recv Buffer) 有足夠空間

3、接收方 Kernel 通告一個較大的接收窗口 (rwnd

[概念圖示 - 階段 1]

+-----------------+        +-----------------+
|  Sender Kernel  |        | Receiver Kernel |
| Send Buffer:    | -----> | Recv Buffer:    | <---- Receiver App reads
| [Some data]     |        | [Some data]     |
+-----------------+        | Capacity: High  |
                         | Free:    High   |
                         +-----------------+
                                  |
                                  -----> ACK (rwnd = High) back to Sender

階段 2: 接收方 App 不調用 recv(),緩衝區開始填滿

1、發送方繼續發送數據

2、接收方 Kernel 接收數據,存入 Recv Buffer

3、接收方 App 沒有調用 recv() 讀取數據

4、Recv Buffer 中的數據越積越多,可用空間減少

5、接收方 Kernel 發送的 ACK 中 rwnd 值開始減小

[概念圖示 - 階段 2]

+-----------------+        +-----------------------+
|  Sender Kernel  |        |   Receiver Kernel     |
| Send Buffer:    | -----> | Recv Buffer:          | <-- App NOT reading
| [Data waiting]  |        | [data3|data4|.......] |
+-----------------+        | Capacity: High        |
                         | Free:    Low / Small  |
                         +-----------------------+
                                    |
                                    -----> ACK (rwnd = Small) back to Sender

階段 3: 接收緩衝區滿,通告零窗口 (Zero Window)

1、發送方又發送了一個小數據段,正好填滿了接收緩衝區

2、接收方 Kernel 接收最後的數據,Recv Buffer 滿了

3、接收方 Kernel 發送 ACK,rwnd 值為 0

[概念圖示 - 階段 3]

+-----------------+        +-----------------------+
|  Sender Kernel  |        |   Receiver Kernel     |
| Send Buffer:    | --X--> | Recv Buffer:          | <-- App NOT reading
| [More data...]  | (Stops)| [data3|data4|data5|FULL] |
+-----------------+        | Capacity: High        |
                         | Free:    Zero         |
                         +-----------------------+
                                    |
                                    -----> ACK (rwnd = 0) back to Sender

階段 4: 發送方停止發送,進入持續狀態 (Persist State)

1、發送方 Kernel 收到 rwnd=0 的 ACK

2、發送方 Kernel 停止發送新的數據段 (Send Buffer 中的數據等待)

3、如果發送方 App 繼續調用 send(),數據會堆積在 Send Buffer;如果 Send Buffer 也滿了,send() 調用會阻塞 (或返回 EAGAIN/EWOULDBLOCK)

4、發送方 Kernel 啓動 Persist Timer

階段 5: 窗口探測 (Window Probe)

1、Persist Timer 超時

2、發送方 Kernel 發送一個小的 Window Probe 包 (通常是 1 字節數據或純 ACK)

3、接收方 Kernel 必須響應這個 Probe,回覆當前的 rwnd (此時仍然是 0)

4、發送方 Kernel 收到 rwnd=0 的 ACK,重置 Persist Timer (超時時間通常會指數增長)

階段 6: 接收方 App 調用 recv(),恢復傳輸

1、接收方 App 終於調用 recv(),從 Recv Buffer 讀取數據

2、Recv Buffer 騰出空間

3、當接收方 Kernel 下次發送 ACK 時 (可能是對 Window Probe 的響應,或對重傳數據的響應),會包含一個非零的 rwnd

4、發送方 Kernel 收到非零 rwnd 的 ACK

5、發送方 Kernel 恢復數據發送 (根據新的 rwnd 大小)

6、如果發送方 App 的 send() 之前被阻塞,現在可能會解除阻塞

[概念圖示 - 階段 6]

+-----------------+        +-----------------------+
|  Sender Kernel  |        |   Receiver Kernel     |
| Send Buffer:    | -----> | Recv Buffer:          | <---- App finally reads!
| [Sending again] |        | [.......|data5| Free ] |
+-----------------+        | Capacity: High        |
                         | Free:    Non-Zero     |
                         +-----------------------+
                                    |
                                    -----> ACK (rwnd = Non-Zero) back to Sender

總結:

接收方不調用 recv() 主要導致:

  1. 接收緩衝區填滿。
  2. TCP 流量控制機制生效,接收方通告零窗口 (rwnd=0)。

3) 發送方暫停數據發送,並進入持續狀態,定期發送窗口探測包。

4) 連接本身通常不會因此立即斷開(除非有其他超時機制介入,如 Keep-Alive 超時),但數據傳輸會完全停止,直到接收方應用程序讀取數據,接收緩衝區騰出空間,並通告非零窗口。

  1. 發送方應用程序的 send() 調用最終可能會阻塞或失敗。

這種情況是 TCP 健壯性的體現,它確保了快速的發送方不會淹沒慢速的接收方,防止了數據丟失和資源耗盡。但也可能導致應用程序層面的"卡死"或性能下降,如果接收方長時間不處理數據的話。

Add a new Comments

Some HTML is okay.