絡(luò)編程》深入理解 TCP 協(xié)議(二):序號(hào)、確認(rèn)應(yīng)答與流量控制機(jī)制詳解)
小葉-duck個(gè)人主頁(yè)??個(gè)人專欄《Data-Structure-Learning》《C入門到進(jìn)階自我學(xué)習(xí)過程記錄》《Linux系統(tǒng)從入門到實(shí)踐》《Linux網(wǎng)絡(luò)從入門到實(shí)踐》《Qt 方寸極境》 《MySQL》?未擇之路不須回頭已擇之路縱是荊棘遍野亦作花海遨游目錄前言一、TCP 核心知識(shí)點(diǎn)回顧二、 TCP 發(fā)送數(shù)據(jù)的兩種工作模式2.1 串行發(fā)送模式停等協(xié)議 Stop-and-Wait2.2 并行發(fā)送模式流水線模式 Pipelining三、TCP 序號(hào)與確認(rèn)序號(hào)機(jī)制3.1 序號(hào)的本質(zhì)3.2 確認(rèn)序號(hào)定義與累積確認(rèn)機(jī)制3.3 序號(hào)的三大核心功能3.4 緩沖區(qū)與序號(hào)的邏輯關(guān)系3.5 經(jīng)典場(chǎng)景中間報(bào)文丟包的處理3.5.1 前置基礎(chǔ)定義3.5.2 正常無丟包場(chǎng)景的序號(hào)流轉(zhuǎn)3.5.3 報(bào)文 3 丟失、報(bào)文 4 亂序提前到達(dá)場(chǎng)景3.5.4 補(bǔ)齊缺失報(bào)文后的確認(rèn)應(yīng)答3.5.5 核心結(jié)論3.6 問題為什么同時(shí)需要序號(hào)和確認(rèn)序號(hào)3.6.1 全雙工通信要求雙方都能發(fā)送和確認(rèn)3.6.2 捎帶應(yīng)答一個(gè)報(bào)文同時(shí)承載數(shù)據(jù)和 ACK3.6.3 區(qū)分捎帶應(yīng)答純ACK應(yīng)答3.6.4 小結(jié)四、TCP 16 位窗口大小與流量控制4.1 流量控制產(chǎn)生背景4.2 接收方接收能力的量化標(biāo)準(zhǔn)4.3 16 位窗口大小字段含義4.4 流量控制的本質(zhì)提高效率4.5 窗口探測(cè)機(jī)制零窗口死鎖規(guī)避4.5.1 窗口探測(cè)視角理解TCP面向字節(jié)流4.6 核心總結(jié)結(jié)束語(yǔ)前言在上一篇文章中我們深度解析了 TCP 報(bào)頭中 4 位首部長(zhǎng)度字段的設(shè)計(jì)精髓以及可靠性最底層的兩大基石 —— 確認(rèn)應(yīng)答與超時(shí)重傳機(jī)制。但 TCP 的復(fù)雜之處遠(yuǎn)不止于此為了提高傳輸效率TCP 不會(huì)像停等協(xié)議那樣發(fā)一個(gè)等一個(gè)而是支持并行發(fā)送多個(gè)報(bào)文面對(duì)多個(gè)報(bào)文同時(shí)發(fā)送帶來的丟包、亂序、重復(fù)等問題TCP 依靠序號(hào)與確認(rèn)序號(hào)這套字節(jié)級(jí)編號(hào)體系來化解為了避免發(fā)送方發(fā)送過快導(dǎo)致接收方緩沖區(qū)溢出TCP 引入了流量控制機(jī)制并借助 16 位窗口大小字段實(shí)時(shí)同步雙方的接收能力。本文將繼續(xù)沿著 TCP 協(xié)議的設(shè)計(jì)思路往下走先從數(shù)據(jù)發(fā)送的兩種工作模式講起厘清停等協(xié)議與流水線模式的取舍再深度拆解序號(hào)與確認(rèn)序號(hào)的核心作用還原丟包、亂序場(chǎng)景下 TCP 的真實(shí)處理邏輯隨后詳解流量控制與窗口探測(cè)機(jī)制。所有內(nèi)容均嚴(yán)格基于 TCP 協(xié)議規(guī)范和 Linux 內(nèi)核實(shí)現(xiàn)力求做到理論與實(shí)踐相結(jié)合。一、TCP 核心知識(shí)點(diǎn)回顧在開始本篇文章新內(nèi)容之前我們先梳理上篇文章中TCP的基礎(chǔ)核心結(jié)論作為后續(xù)內(nèi)容理解的前提報(bào)文交互規(guī)則TCP 通信雙方交互的基本單元是完整 TCP 報(bào)文。發(fā)送方傳輸業(yè)務(wù)數(shù)據(jù)時(shí)數(shù)據(jù)會(huì)封裝成 TCP 報(bào)文接收方返回的應(yīng)答報(bào)文就算不攜帶任何應(yīng)用層數(shù)據(jù)也必須保留完整 TCP 報(bào)頭不會(huì)單獨(dú)只發(fā)送標(biāo)志位。確認(rèn)應(yīng)答核心規(guī)則TCP 屬于全雙工可靠傳輸協(xié)議確認(rèn)應(yīng)答是實(shí)現(xiàn)可靠性的基礎(chǔ)。應(yīng)答報(bào)文由接收端操作系統(tǒng)內(nèi)核的 TCP 協(xié)議棧自動(dòng)生成并回復(fù)不需要應(yīng)用層參與。 通信規(guī)則A 向 B 發(fā)送 TCP 數(shù)據(jù)B 收到后自動(dòng)返回 ACK 應(yīng)答應(yīng)答報(bào)文本身不再需要二次應(yīng)答否則會(huì)形成無限循環(huán)嚴(yán)重降低傳輸效率。依托這套機(jī)制通信雙向都可以保障數(shù)據(jù)傳輸可靠。超時(shí)重傳判定邏輯發(fā)送方收到 ACK 應(yīng)答就可以 100% 確認(rèn)接收方完整收到數(shù)據(jù)本次傳輸可靠。 如果發(fā)送方?jīng)]有收到應(yīng)答無法區(qū)分是原始數(shù)據(jù)丟包還是 ACK 應(yīng)答報(bào)文在路上丟失。TCP 統(tǒng)一處理超時(shí)時(shí)間內(nèi)沒有收到對(duì)應(yīng)應(yīng)答就判定傳輸異常自動(dòng)觸發(fā)報(bào)文重傳。TCP 可靠性的真正定義TCP 的可靠不等于強(qiáng)制保證數(shù)據(jù)一定送達(dá)接收方。可靠性的核心本質(zhì)是無論傳輸最終成功還是失敗發(fā)送方一定能夠感知最終結(jié)果。收到 ACK 代表數(shù)據(jù)交付成功超時(shí)無應(yīng)答則判定傳輸失敗并重傳。就算遇到網(wǎng)線斷開、網(wǎng)絡(luò)中斷這類場(chǎng)景TCP 也可以精準(zhǔn)感知傳輸失敗的狀態(tài)。全雙工特性TCP 是全雙工協(xié)議通信兩端能夠同時(shí)收發(fā)數(shù)據(jù)。這個(gè)特性深刻影響 TCP 的諸多設(shè)計(jì)捎帶應(yīng)答、三次握手等機(jī)制都建立在全雙工的基礎(chǔ)之上。二、 TCP 發(fā)送數(shù)據(jù)的兩種工作模式TCP 為適配不同的數(shù)據(jù)傳輸場(chǎng)景設(shè)計(jì)了兩套發(fā)送工作模式串行發(fā)送與并行流水線發(fā)送模式。2.1 串行發(fā)送模式停等協(xié)議 Stop-and-Wait工作邏輯發(fā)送方每發(fā)出 1 個(gè)報(bào)文之后就暫停發(fā)送原地等待接收方返回對(duì)應(yīng)的 ACK 應(yīng)答。必須收到應(yīng)答才可以繼續(xù)發(fā)送下一份報(bào)文。優(yōu)點(diǎn)邏輯簡(jiǎn)單天然不會(huì)出現(xiàn)報(bào)文亂序、重復(fù)接收的問題實(shí)現(xiàn)成本低。缺點(diǎn)傳輸效率很低。在等待 ACK 的 RTT 往返時(shí)間內(nèi)網(wǎng)絡(luò)鏈路處于空閑狀態(tài)帶寬資源被浪費(fèi)。適用場(chǎng)景只適合少量數(shù)據(jù)傳輸場(chǎng)景在真實(shí) TCP 通信里很少單獨(dú)使用。主機(jī)A 主機(jī)B |---- 數(shù)據(jù)1 ------| | | |---- ACK1 -------| | | |---- 數(shù)據(jù)2 ------| | | |---- ACK2 -------|2.2 并行發(fā)送模式流水線模式 Pipelining這是 TCP 實(shí)際通信里主流默認(rèn)使用的發(fā)送模式。工作邏輯發(fā)送方不需要等待上一個(gè)報(bào)文的 ACK 應(yīng)答就可以連續(xù)向外發(fā)送多個(gè)報(bào)文不需要等待一輪 RTT。多個(gè)報(bào)文的收發(fā)時(shí)間可以相互重疊充分利用鏈路帶寬大幅提升網(wǎng)絡(luò)吞吐與傳輸效率。主機(jī)A 主機(jī)B |------ 數(shù)據(jù)1 ------| |------ 數(shù)據(jù)2 ------| |------ 數(shù)據(jù)3 ------| |----- ACK1 -------| |----- ACK2 -------| |------ 數(shù)據(jù)4 ------| |----- ACK3 -------|并行流水線模式雖然解決了性能瓶頸但同時(shí)引入了新難題如果多個(gè)報(bào)文連續(xù)發(fā)出中間某個(gè)報(bào)文丟失發(fā)送方如何定位到底是哪一段數(shù)據(jù)丟包報(bào)文在網(wǎng)絡(luò)路由轉(zhuǎn)發(fā)時(shí)亂序到達(dá)接收端怎么還原出原始數(shù)據(jù)流順序超時(shí)重傳帶來重復(fù)報(bào)文接收端如何識(shí)別并丟棄重復(fù)數(shù)據(jù)為了解決并行流水線帶來的丟包識(shí)別、報(bào)文排序、去重等一系列問題TCP 協(xié)議在報(bào)頭中定義了兩個(gè)核心字段32 位序號(hào)Sequence Number和32 位確認(rèn)序號(hào)Acknowledgment Number。三、TCP 序號(hào)與確認(rèn)序號(hào)機(jī)制序號(hào)與確認(rèn)序號(hào)是 TCP 實(shí)現(xiàn)可靠傳輸與流水線高效傳輸?shù)暮诵腡CP 絕大多數(shù)可靠性機(jī)制都建立在這兩個(gè)字段之上。3.1 序號(hào)的本質(zhì)操作系統(tǒng)內(nèi)核維護(hù)發(fā)送緩沖區(qū)與接收緩沖區(qū)底層依靠sk_buff存儲(chǔ)報(bào)文。邏輯層面TCP 把緩沖區(qū)中的數(shù)據(jù)流視為一個(gè)巨大連續(xù)字節(jié)數(shù)組序號(hào)就是這個(gè)字節(jié)數(shù)組的下標(biāo)。TCP 不會(huì)逐字節(jié)發(fā)送數(shù)據(jù)而是將數(shù)據(jù)分批封裝成 TCP 數(shù)據(jù)段Segment進(jìn)行傳輸。舉例報(bào)文承載第 11000 字節(jié)數(shù)據(jù)該報(bào)文的序號(hào) 1下一段承載 10012000 字節(jié)序號(hào) 1001再下一段承載 20013000 字節(jié)序號(hào) 20013.2 確認(rèn)序號(hào)定義與累積確認(rèn)機(jī)制確認(rèn)序號(hào)計(jì)算公式確認(rèn)序號(hào) 收到的最后一個(gè)完整字節(jié)的序號(hào) 1含義確認(rèn)序號(hào) N代表 N 之前所有字節(jié)全部接收完畢下一次期望從序號(hào) N 開始接收數(shù)據(jù)。示例主機(jī) B 收到 1~1000 字節(jié)的數(shù)據(jù)段回復(fù)確認(rèn)序號(hào) 1001主機(jī) B 收到 1001~2000 字節(jié)的數(shù)據(jù)段回復(fù)確認(rèn)序號(hào) 2001。TCP 采用累積確認(rèn)這是非常關(guān)鍵的特性 如果主機(jī) B 已經(jīng)收到 1~1000、2001~3000但中間 1001~2000 還未收到此時(shí)只能回復(fù)確認(rèn)序號(hào) 1001。 哪怕后面的字節(jié)提前到達(dá)也不會(huì)確認(rèn)后面的數(shù)據(jù)以此保障 TCP 有序交付。累積確認(rèn)優(yōu)勢(shì)容錯(cuò)能力強(qiáng)中間部分 ACK 報(bào)文在網(wǎng)絡(luò)丟失也不會(huì)造成嚴(yán)重影響只要后續(xù) ACK 抵達(dá)發(fā)送方就能確認(rèn)數(shù)據(jù)。3.3 序號(hào)的三大核心功能正是依靠序號(hào)和確認(rèn)序號(hào)流水線并行發(fā)送帶來的各類難題得以解決保障傳輸可靠性接收方通過確認(rèn)序號(hào)反饋接收進(jìn)度發(fā)送方能精準(zhǔn)判斷哪些數(shù)據(jù)成功送達(dá)、哪些報(bào)文丟失觸發(fā)超時(shí)重傳。報(bào)文去重超時(shí)重傳場(chǎng)景下接收端可能收到重復(fù)報(bào)文依靠序號(hào)識(shí)別重復(fù)數(shù)據(jù)并丟棄。實(shí)現(xiàn)亂序重組網(wǎng)絡(luò)傳輸時(shí)報(bào)文可能亂序抵達(dá)接收緩沖區(qū)利用序號(hào)重新排序整理成有序數(shù)據(jù)流再交付應(yīng)用層。3.4 緩沖區(qū)與序號(hào)的邏輯關(guān)系發(fā)送緩沖區(qū)、接收緩沖區(qū)在內(nèi)核中以 sk_buff 隊(duì)列物理存儲(chǔ)。邏輯上等效為連續(xù)字節(jié)數(shù)組緩沖區(qū)里的每一字節(jié)數(shù)據(jù)都對(duì)應(yīng)唯一序號(hào)下標(biāo)。 TCP 數(shù)據(jù)段發(fā)送時(shí)按字節(jié)范圍打包。例如一段報(bào)文包含 1000 字節(jié)起始序號(hào) 1覆蓋字節(jié) 1~1000下一段就從 1001 開始。不同數(shù)據(jù)段的序號(hào)自然不連續(xù)根源是每個(gè) TCP 段攜帶的數(shù)據(jù)長(zhǎng)度不一樣。3.5 經(jīng)典場(chǎng)景中間報(bào)文丟包的處理場(chǎng)景發(fā)送方依次發(fā)送序號(hào) 100、200、300、400 四段報(bào)文序號(hào) 300 的報(bào)文在傳輸途中丟失400 報(bào)文先抵達(dá)接收端。此時(shí)接收方不能直接應(yīng)答 400仍然持續(xù)回復(fù)確認(rèn)序號(hào) 300。即便收到 400 這一段接收端也會(huì)告知發(fā)送方300 之前的數(shù)據(jù)已經(jīng)全部收到請(qǐng)從 300 繼續(xù)發(fā)送。只有 300 號(hào)報(bào)文補(bǔ)齊之后確認(rèn)序號(hào)才會(huì)繼續(xù)向后推進(jìn)。面試小結(jié)確認(rèn)序號(hào)只確認(rèn)連續(xù)的前置字節(jié)后面提前到達(dá)的數(shù)據(jù)不會(huì)單獨(dú)確認(rèn)這就是累積確認(rèn)。3.5.1 前置基礎(chǔ)定義TCP 是面向字節(jié)流的協(xié)議全部數(shù)據(jù)都會(huì)按字節(jié)分配連續(xù)序號(hào)序號(hào)對(duì)應(yīng)內(nèi)核接收緩沖區(qū)的字節(jié)下標(biāo)。報(bào)文起始序號(hào)該報(bào)文攜帶的第一個(gè)字節(jié)的下標(biāo)報(bào)文結(jié)束序號(hào)該報(bào)文攜帶的最后一個(gè)字節(jié)的下標(biāo) 起始序號(hào) 報(bào)文長(zhǎng)度 - 1確認(rèn)序號(hào) ACK接收方返回期望收到的下一個(gè)字節(jié)下標(biāo)含義我已經(jīng)完整收到 ACK 序號(hào)之前的全部字節(jié)這就是 TCP 累積確認(rèn)的核心規(guī)則。3.5.2 正常無丟包場(chǎng)景的序號(hào)流轉(zhuǎn)連續(xù)發(fā)送 4 個(gè)報(bào)文每個(gè)報(bào)文固定承載 100 字節(jié)數(shù)據(jù)報(bào)文編號(hào)起始序號(hào)攜帶字節(jié)范圍結(jié)束序號(hào)下一個(gè)報(bào)文起始序號(hào)報(bào)文 111~100100101報(bào)文 2101101~200200201報(bào)文 3201201~300300301報(bào)文 4301301~400400401網(wǎng)絡(luò)正常無丟包、不亂序時(shí)接收方收到報(bào)文后回復(fù)對(duì)應(yīng)的確認(rèn)序號(hào)收到報(bào)文 1 → 返回 ACK1011~100 字節(jié)已全部接收下次從 101 開始發(fā)送收到報(bào)文 2 → 返回 ACK2011~200 字節(jié)已全部接收下次從 201 開始發(fā)送收到報(bào)文 3 → 返回 ACK3011~300 字節(jié)已全部接收下次從 301 開始發(fā)送收到報(bào)文 4 → 返回 ACK4011~400 字節(jié)已全部接收下次從 401 開始發(fā)送3.5.3 報(bào)文 3 丟失、報(bào)文 4 亂序提前到達(dá)場(chǎng)景發(fā)送方按順序發(fā)送報(bào)文 1 → 報(bào)文 2 → 報(bào)文 3 → 報(bào)文 4。 網(wǎng)絡(luò)傳輸發(fā)生異常報(bào)文 1、報(bào)文 2 正常抵達(dá)接收端報(bào)文 3201~300 字節(jié)在傳輸途中丟失報(bào)文 4301~400 字節(jié)繞過路由先于報(bào)文 3 到達(dá)接收方。接收方緩沖區(qū)當(dāng)前狀態(tài)? 連續(xù)完整收到1~200 字節(jié)報(bào)文 1、報(bào)文 2? 提前零散收到301~400 字節(jié)報(bào)文 4臨時(shí)存放在接收緩沖區(qū)? 缺失空缺段201~300 字節(jié)報(bào)文 3重點(diǎn)說明 雖然報(bào)文 4 已經(jīng)提前到達(dá)并存入接收緩沖區(qū)接收方依然只能回復(fù) ACK201不能返回 ACK401。累積確認(rèn)有硬性約束確認(rèn)序號(hào)只能標(biāo)記已經(jīng)連續(xù)收到的最大字節(jié)的下一位不能跳過中間缺失的字節(jié)。如果返回 ACK401會(huì)誤導(dǎo)發(fā)送方讓發(fā)送方認(rèn)為 1~400 全部收到不會(huì)重傳丟失的 201~300 這一段最終造成數(shù)據(jù)永久丟失。接收端只會(huì)暫存提前抵達(dá)的報(bào)文 4但不會(huì)更新確認(rèn)序號(hào)必須等空缺的 201~300 字節(jié)補(bǔ)齊之后確認(rèn)序號(hào)才會(huì)向后推進(jìn)。 懸念此時(shí)發(fā)送方收到 ACK201 重復(fù)應(yīng)答如何判斷是報(bào)文 3 發(fā)生丟失、需要重傳報(bào)文 3該部分依賴滑動(dòng)窗口、快速重傳機(jī)制我們留到后面章節(jié)詳細(xì)講解。3.5.4 補(bǔ)齊缺失報(bào)文后的確認(rèn)應(yīng)答發(fā)送方觸發(fā)重傳重新發(fā)送報(bào)文 3201~300 字節(jié)。接收端收到重傳的報(bào)文 3 后緩沖區(qū) 1~400 字節(jié)全部補(bǔ)齊此時(shí)接收方返回 ACK401。ACK401 屬于累積確認(rèn)它的含義是1~400 的所有字節(jié)我都完整收到。中間的 ACK201、ACK301 應(yīng)答報(bào)文就算在網(wǎng)絡(luò)中丟失也沒關(guān)系只要發(fā)送方收到最高確認(rèn)序號(hào) ACK401就代表前置全部字節(jié)送達(dá)不需要重復(fù)重傳任何報(bào)文直接從 401 序號(hào)繼續(xù)傳輸后續(xù)數(shù)據(jù)3.5.5 核心結(jié)論TCP 序號(hào)是字節(jié)級(jí)編號(hào)不是報(bào)文編號(hào)報(bào)文之間序號(hào)不連續(xù)是因?yàn)槊總€(gè)報(bào)文承載多個(gè)字節(jié)。確認(rèn)序號(hào)永遠(yuǎn)等于已連續(xù)接收的最后一個(gè)字節(jié)序號(hào) 1。亂序提前到達(dá)的報(bào)文會(huì)被接收緩沖區(qū)暫存但確認(rèn)序號(hào)不會(huì)向前推進(jìn)直到空缺字節(jié)補(bǔ)齊(重點(diǎn)理解)。累積確認(rèn)自帶容錯(cuò)能力中間 ACK 應(yīng)答丟失不影響傳輸只要收到最高序號(hào)的確認(rèn)就代表前面所有數(shù)據(jù)全部接收成功。3.6 問題為什么同時(shí)需要序號(hào)和確認(rèn)序號(hào)在前面的例子里我們看到確認(rèn)序號(hào)可以表示 “我已經(jīng)收到了哪些字節(jié)”。于是有人會(huì)問既然確認(rèn)序號(hào)已經(jīng)能說明接收進(jìn)度為什么不直接只用一個(gè)32位序號(hào)字段應(yīng)答時(shí)把序號(hào)加 1 再添到32位序號(hào)中返回不就行了這個(gè)問題的關(guān)鍵在于TCP 是全雙工協(xié)議通信雙方可以同時(shí)發(fā)送數(shù)據(jù)。在這種情況下發(fā)送方不僅要標(biāo)識(shí) “自己發(fā)的數(shù)據(jù)到了哪里”還要標(biāo)識(shí) “自己確認(rèn)收到了對(duì)方哪些數(shù)據(jù)”。3.6.1 全雙工通信要求雙方都能發(fā)送和確認(rèn)TCP 允許兩端同時(shí)傳輸數(shù)據(jù)。主機(jī) A 可以向主機(jī) B 發(fā)送數(shù)據(jù)主機(jī) B 也可以同時(shí)向主機(jī) A 發(fā)送數(shù)據(jù)。這意味著每一端都需要一個(gè) “發(fā)送序號(hào)”用來標(biāo)記自己發(fā)送數(shù)據(jù)的字節(jié)位置每一端也需要一個(gè) “確認(rèn)序號(hào)”用來告訴對(duì)方我已經(jīng)收到了你的哪些數(shù)據(jù)。如果只有一個(gè)序號(hào)字段就無法同時(shí)區(qū)分當(dāng)前報(bào)文是在描述 “我發(fā)送到哪里”還是在描述 “我確認(rèn)收到了哪里”。因此序號(hào)和確認(rèn)序號(hào)是兩個(gè)獨(dú)立職責(zé)字段作用序號(hào)標(biāo)識(shí)本報(bào)文攜帶的數(shù)據(jù)在發(fā)送方字節(jié)流中的位置確認(rèn)序號(hào)標(biāo)識(shí)接收方已經(jīng)連續(xù)收到的最大字節(jié)位置3.6.2 捎帶應(yīng)答一個(gè)報(bào)文同時(shí)承載數(shù)據(jù)和 ACKTCP 的全雙工特性帶來了一個(gè)重要優(yōu)化捎帶應(yīng)答。當(dāng)主機(jī) B 需要向主機(jī) A 發(fā)送數(shù)據(jù)時(shí)它不必單獨(dú)發(fā)送一條空的 ACK 報(bào)文而是可以把對(duì)主機(jī) A 的確認(rèn)信息直接放到自己的業(yè)務(wù)數(shù)據(jù)報(bào)文中一起發(fā)送。例如主機(jī) A 先向主機(jī) B 發(fā)送數(shù)據(jù)字節(jié)范圍是 11000主機(jī) B 收到后本來可以返回一個(gè) ACK1001但如果此時(shí)主機(jī) B 也有數(shù)據(jù)要發(fā)給主機(jī) A比如字節(jié)范圍是 50016000那么主機(jī) B 可以直接發(fā)送一個(gè)同時(shí)包含數(shù)據(jù)的報(bào)文自己的發(fā)送序號(hào)5001對(duì)主機(jī) A 的確認(rèn)序號(hào)1001。這樣這個(gè)報(bào)文既攜帶了主機(jī) B 的業(yè)務(wù)數(shù)據(jù)又完成了對(duì)主機(jī) A 的確認(rèn)減少了單獨(dú) ACK 報(bào)文的數(shù)量提高了傳輸效率。3.6.3 區(qū)分捎帶應(yīng)答純ACK應(yīng)答引出問題3.6.4 小結(jié)TCP 之所以同時(shí)需要序號(hào)和確認(rèn)序號(hào)根本原因有兩個(gè)TCP 是全雙工協(xié)議兩端可以同時(shí)發(fā)送數(shù)據(jù)必須分別跟蹤各自的發(fā)送進(jìn)度TCP 支持捎帶應(yīng)答一個(gè)報(bào)文可以同時(shí)承載業(yè)務(wù)數(shù)據(jù)和確認(rèn)信息因此需要同時(shí)標(biāo)識(shí) “我發(fā)的數(shù)據(jù)” 和 “我確認(rèn)的數(shù)據(jù)”。這也進(jìn)一步說明序號(hào)和確認(rèn)序號(hào)并不是兩個(gè)孤立的數(shù)字而是 TCP 可靠性、順序控制和流量控制的重要基礎(chǔ)。后續(xù)講解滑動(dòng)窗口時(shí)還會(huì)看到這兩個(gè)字段如何配合窗口大小共同決定發(fā)送方可以連續(xù)發(fā)送多少數(shù)據(jù)。四、TCP 16 位窗口大小與流量控制4.1 流量控制產(chǎn)生背景即便網(wǎng)絡(luò)帶寬充足發(fā)送方也不能無限制持續(xù)發(fā)送數(shù)據(jù)。接收主機(jī)的處理能力存在上限如果發(fā)送速率過快內(nèi)核接收緩沖區(qū)會(huì)被迅速填滿后續(xù)到達(dá)的數(shù)據(jù)會(huì)直接丟棄。數(shù)據(jù)包一旦丟失會(huì)觸發(fā) TCP 超時(shí)重傳反復(fù)重傳會(huì)浪費(fèi)網(wǎng)絡(luò)帶寬、主機(jī) CPU 以及內(nèi)存資源傳輸效率大幅下降。TCP 引入流量控制解決這個(gè)問題而 TCP 頭部的16 位窗口大小字段就是流量控制的核心載體。4.2 接收方接收能力的量化標(biāo)準(zhǔn)接收方的接收能力由內(nèi)核接收緩沖區(qū)的剩余空閑空間決定接收緩沖區(qū)的剩余空間越大代表接收能力越強(qiáng)可以接收更多數(shù)據(jù)接收緩沖區(qū)的剩余空間越小接收能力越弱接收緩沖區(qū)的剩余空間為 0緩沖區(qū)已滿無法接收新數(shù)據(jù)。4.3 16 位窗口大小字段含義窗口大小字段表示接收方當(dāng)前接收緩沖區(qū)剩余空閑字節(jié)數(shù)量單位是字節(jié)。 接收方在回復(fù) ACK 應(yīng)答報(bào)文時(shí)會(huì)把自身接收緩沖區(qū)剩余空間寫入窗口大小字段告訴發(fā)送方當(dāng)前還能接收多少字節(jié)的數(shù)據(jù)。 發(fā)送方讀取 ACK 中的窗口值動(dòng)態(tài)調(diào)整發(fā)送速度窗口大接收方處理壓力小發(fā)送方可以持續(xù)發(fā)送較多數(shù)據(jù)窗口小接收方緩沖區(qū)剩余空間緊張發(fā)送方需要降低發(fā)送速率窗口為 0接收緩沖區(qū)已滿發(fā)送方必須暫停發(fā)送業(yè)務(wù)數(shù)據(jù)。計(jì)算公式窗口大小 接收緩沖區(qū)總大小 - 緩沖區(qū)已使用字節(jié)數(shù)4.4 流量控制的本質(zhì)提高效率4.5 窗口探測(cè)機(jī)制零窗口死鎖規(guī)避當(dāng)接收方窗口等于 0發(fā)送方停止發(fā)送業(yè)務(wù)數(shù)據(jù)。 這里存在一個(gè)風(fēng)險(xiǎn)接收方后續(xù)窗口更新的 ACK 報(bào)文如果在網(wǎng)絡(luò)傳輸中丟失發(fā)送方會(huì)一直阻塞等待雙方陷入死鎖。TCP 引入窗口探測(cè)機(jī)制解決該問題 發(fā)送方收到窗口為 0 的 ACK 后會(huì)周期性發(fā)送僅攜帶 1 字節(jié)數(shù)據(jù)的窗口探測(cè)報(bào)文用來詢問接收方當(dāng)前最新窗口大小。如果接收方窗口已經(jīng)恢復(fù)返回?cái)y帶新窗口值的 ACK發(fā)送方恢復(fù)數(shù)據(jù)傳輸如果窗口依舊為 0接收方回復(fù)窗口 0發(fā)送方繼續(xù)等待下一次周期再探測(cè)。4.5.1 窗口探測(cè)視角理解TCP面向字節(jié)流4.6 核心總結(jié)流量控制解決發(fā)送方和接收方主機(jī)處理能力不匹配的問題和網(wǎng)絡(luò)擁塞不是一回事?lián)砣刂坪罄m(xù)講解16 位窗口大小由接收方填充反映接收緩沖區(qū)剩余空間用于告知發(fā)送方接收上限窗口為 0 時(shí)發(fā)送方暫停發(fā)送依靠窗口探測(cè)報(bào)文防止窗口更新報(bào)文丟失帶來的死鎖問題流量控制全程動(dòng)態(tài)交互讓發(fā)送速率跟隨接收方處理能力自適應(yīng)變化減少丟包與不必要的重傳。結(jié)束語(yǔ)回顧本篇我們承接上一篇文章對(duì)確認(rèn)應(yīng)答與超時(shí)重傳的講解把視角從 數(shù)據(jù)怎么保證送達(dá) 推進(jìn)到 數(shù)據(jù)怎么傳得又快又穩(wěn)。我們先是理清了停等協(xié)議與流水線兩種發(fā)送模式的區(qū)別明白 TCP 之所以默認(rèn)采用并行發(fā)送是為了消除等待應(yīng)答的往返空檔充分榨取鏈路帶寬。隨之而來的丟包定位、亂序重組、重復(fù)報(bào)文識(shí)別則依靠序號(hào)與確認(rèn)序號(hào)這套字節(jié)級(jí)編號(hào)體系解決。通過丟包場(chǎng)景的推演我們看到累積確認(rèn)如何保證有序交付也理解了為什么序號(hào)和確認(rèn)序號(hào)兩個(gè)字段缺一不可。最后流量控制機(jī)制借助 16 位窗口字段動(dòng)態(tài)調(diào)節(jié)發(fā)送速率解決了接收方緩沖區(qū)溢出的隱患窗口探測(cè)則化解了零窗口下的死鎖風(fēng)險(xiǎn)。值得注意的是流量控制針對(duì)的是收發(fā)兩端處理能力不匹配的問題與后續(xù)要講的擁塞控制并非同一概念。序號(hào)、窗口、重傳這些機(jī)制環(huán)環(huán)相扣共同撐起 TCP 復(fù)雜而嚴(yán)謹(jǐn)?shù)膫鬏斂刂企w系也為下一部分學(xué)習(xí)三次握手、四次揮手與連接管理打下了堅(jiān)實(shí)基礎(chǔ)。