當 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的 ACK2、發送方 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 的響應,或對重傳數據的響應),會包含一個非零的
rwnd4、發送方 Kernel 收到非零
rwnd的 ACK5、發送方 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() 主要導致:
- 接收緩衝區填滿。
- TCP 流量控制機制生效,接收方通告零窗口 (
rwnd=0)。
3) 發送方暫停數據發送,並進入持續狀態,定期發送窗口探測包。
4) 連接本身通常不會因此立即斷開(除非有其他超時機制介入,如 Keep-Alive 超時),但數據傳輸會完全停止,直到接收方應用程序讀取數據,接收緩衝區騰出空間,並通告非零窗口。
- 發送方應用程序的
send()調用最終可能會阻塞或失敗。
這種情況是 TCP 健壯性的體現,它確保了快速的發送方不會淹沒慢速的接收方,防止了數據丟失和資源耗盡。但也可能導致應用程序層面的"卡死"或性能下降,如果接收方長時間不處理數據的話。