為什么 TCP/IP 協(xié)議會拆分數(shù)據(jù)
TCP/IP 協(xié)議簇建立了互聯(lián)網(wǎng)通信協(xié)議的概念模型,該協(xié)議簇的兩個主要協(xié)議就是 TCP 和 IP 協(xié)議。這兩個協(xié)議不僅能夠保證數(shù)據(jù)會從源機器的源進程發(fā)送到目標機器的目標進程中,還能保證數(shù)據(jù)的不重不漏以及發(fā)送的順序。
圖 1 - TCP/IP 協(xié)議簇
當應(yīng)用層協(xié)議使用 TCP/IP 協(xié)議傳輸數(shù)據(jù)時,TCP/IP 協(xié)議簇可能會將應(yīng)用層發(fā)送的數(shù)據(jù)分成多個包依次發(fā)送,而數(shù)據(jù)的接收方收到的數(shù)據(jù)可能是分段的或者拼接的,所以它需要對接收的數(shù)據(jù)進行拆分或者重組。本文會分別從 IP 協(xié)議和 TCP 協(xié)議兩個角度出發(fā)分析為什么應(yīng)用層寫入的數(shù)據(jù)包會被 TCP/IP 協(xié)議拆分發(fā)送:
- IP 協(xié)議會分片傳輸過大的數(shù)據(jù)包(Packet)避免物理設(shè)備的限制;
- TCP 協(xié)議會分段傳輸過大的數(shù)據(jù)段(Segment)保證傳輸?shù)目煽啃院晚樞?
最大傳輸單元
IP 協(xié)議是用于傳輸數(shù)據(jù)包的協(xié)議,作為網(wǎng)絡(luò)層協(xié)議,它能提供數(shù)據(jù)的路由和尋址功能,讓數(shù)據(jù)通過網(wǎng)絡(luò)到達目的地。不同設(shè)備之間傳輸數(shù)據(jù)前,需要先確定一個 IP 數(shù)據(jù)包的大小上限,即最大傳輸單元(Maximum transmission unit,即 MTU),MTU 是 IP 數(shù)據(jù)包能夠傳輸?shù)臄?shù)據(jù)上限。
MTU 的值不是越大越好,更大的 MTU 意味著更低的額外開銷,更小的 MTU 意味著更低的網(wǎng)絡(luò)延遲。每一個物理設(shè)備都有自己的 MTU,兩個主機之間的 MTU 依賴于底層的網(wǎng)絡(luò)能力,它由整個鏈路上 MTU 最小的物理設(shè)備決定[^3],如下圖所示,網(wǎng)絡(luò)路徑的 MTU 由 MTU 最小的紅色物理設(shè)備決定,即 1000:
圖 2 - 路徑最大傳輸單元發(fā)現(xiàn)
路徑最大傳輸單元發(fā)現(xiàn)(Path MTU Discovery,PMTUD)是用來確定兩個主機傳輸路徑 MTU 的機制,它的工作原理如下[^4]:
(1) 向目的主機發(fā)送 IP 頭中 DF 控制位為 1 的數(shù)據(jù)包,DF 是不分片(Don't Fragment,DF)的縮寫;
(2) 路徑上的網(wǎng)絡(luò)設(shè)備根據(jù)數(shù)據(jù)包的大小和自己的 MTU 做出不同的決定:
- 如果數(shù)據(jù)包大于設(shè)備的 MTU,就會丟棄數(shù)據(jù)包并發(fā)回一個包含該設(shè)備 MTU 的 ICMP 消息;
- 如果數(shù)據(jù)包小于設(shè)備的 MTU,就會繼續(xù)向目的主機傳遞數(shù)據(jù)包;
(3) 源主機收到 ICMP 消息后,會不斷使用新的 MTU 發(fā)送 IP 數(shù)據(jù)包,直到 IP 數(shù)據(jù)包達到目的主機;
ICMP 是互聯(lián)網(wǎng)控制消息協(xié)議(Internet Control Message Protocol,ICMP),它能在 IP 主機之間傳遞控制消息[^5]。
以太網(wǎng)對數(shù)據(jù)幀的限制一般都是 1500 字節(jié)[^6],在一般情況下,IP 主機的路徑 MTU 都是 1500,去掉 IP 首部的 20 字節(jié),如果待傳輸?shù)臄?shù)據(jù)大于 1480 節(jié),那么該 IP 協(xié)議就會將數(shù)據(jù)包分片傳輸。
IP 協(xié)議數(shù)據(jù)分片對傳輸層協(xié)議是透明的,假設(shè)我們使用 UDP 協(xié)議傳輸 2000 字節(jié)的數(shù)據(jù),加上 UDP 8 字節(jié)的協(xié)議頭],IP 協(xié)議需要傳輸 2008 字節(jié)的數(shù)據(jù)。如下圖所示,當 IP 協(xié)議發(fā)現(xiàn)待傳輸?shù)臄?shù)據(jù)大于 1480 字節(jié),就會將數(shù)據(jù)分成下面的兩個數(shù)據(jù)包:
圖 3 - 分片傳輸?shù)?UDP 數(shù)據(jù)
- 20 字節(jié) IP 協(xié)議頭 + 8 字節(jié) UDP 協(xié)議頭 + 1472 字節(jié)數(shù)據(jù);
- 20 字節(jié) IP 協(xié)議頭 + 528 字節(jié)數(shù)據(jù);
數(shù)據(jù)的接收方在收到數(shù)據(jù)包時會對分片的數(shù)據(jù)進行重組,不過因為第二個數(shù)據(jù)包中不包含 UDP 協(xié)議的相關(guān)信息,一旦發(fā)生丟包,整個 UDP 數(shù)據(jù)報就無法重新拼裝。如果 UDP 數(shù)據(jù)報需要傳輸?shù)臄?shù)據(jù)過多,那么 IP 協(xié)議就會大量分片,增加了不穩(wěn)定性。
如果 IP 協(xié)議沒有數(shù)據(jù)包大小的限制,那么上層可以以消息為單位傳輸數(shù)據(jù),自然就不存在分片和組裝的需求,不過因為物理設(shè)備的 MTU 限制,想要保證數(shù)據(jù)傳輸?shù)目煽啃院头€(wěn)定性還需要傳輸層的配合。
最大分段大小
TCP 協(xié)議是面向字節(jié)流的協(xié)議,應(yīng)用層交給 TCP 協(xié)議的數(shù)據(jù)并不會以消息為單位向目的主機發(fā)送,應(yīng)用層交給 TCP 協(xié)議發(fā)送的數(shù)據(jù)可能會被拆分到多個數(shù)據(jù)段中。
TCP 協(xié)議引入了最大分段大小(Maximum segment size,MSS)這一概念,它是 TCP 數(shù)據(jù)段能夠攜帶的數(shù)據(jù)上限[^8]。在正常情況下,TCP 連接的 MSS 是 MTU - 40 字節(jié)[^9],即 1460 字節(jié);不過如果通信雙方?jīng)]有指定 MSS 的話,在默認情況下 MSS 的大小是 536 字節(jié)[^10]。
IP 協(xié)議的 MTU 是物理設(shè)備上的限制,它限制了路徑能夠發(fā)送數(shù)據(jù)包的上限,而 TCP 協(xié)議的 MSS 是操作系統(tǒng)內(nèi)核層面的限制,通信雙方會在三次握手[^11]時確定這次連接的 MSS。一旦確定了 MSS,TCP 協(xié)議就會對應(yīng)用層交給 TCP 協(xié)議發(fā)送的數(shù)據(jù)進行拆分,構(gòu)成多個數(shù)據(jù)段。
需要注意的是,IP 協(xié)議和 TCP 協(xié)議雖然都會對數(shù)據(jù)進行拆分,但是 IP 協(xié)議以數(shù)據(jù)包(Package)為單位組織數(shù)據(jù),而 TCP 協(xié)議以數(shù)據(jù)段(Segment)為單位組織數(shù)據(jù)。
如下圖所示,如果 TCP 連接的 MSS 是 1460 字節(jié),應(yīng)用層想要通過 TCP 協(xié)議傳輸 2000 字節(jié)的數(shù)據(jù),那么 TCP 協(xié)議會根據(jù) MSS 將 2000 字節(jié)的數(shù)據(jù)拆分到兩個數(shù)據(jù)段中:
圖 4 - 分段傳輸?shù)?TCP 數(shù)據(jù)
- 20 字節(jié) IP 頭 + 20 字節(jié) TCP 頭 + 1460 字節(jié)數(shù)據(jù);
- 20 字節(jié) IP 頭 + 20 字節(jié) TCP 頭 + 540 字節(jié)數(shù)據(jù);
從應(yīng)用層的角度來看,兩個數(shù)據(jù)段中 2000 字節(jié)的數(shù)據(jù)構(gòu)成了發(fā)送方想要發(fā)送的消息,但是 TCP 協(xié)議是面向字節(jié)流的,向協(xié)議寫入的數(shù)據(jù)會以流的形式傳遞到對端。
TCP 協(xié)議為了保證可靠性,會通過 IP 協(xié)議的 MTU 計算出 MSS 并根據(jù) MSS 分段避免 IP 協(xié)議對數(shù)據(jù)包進行分片。因為 IP 協(xié)議對數(shù)據(jù)包的分片對上層是透明的,如果協(xié)議不根據(jù) MTU 做一些限制,那么 IP 協(xié)議的分片會導(dǎo)致部分數(shù)據(jù)包失去傳輸層協(xié)議頭,一旦數(shù)據(jù)包發(fā)生丟失就只能丟棄全部數(shù)據(jù)。
我們可以通過一個例子分析 MSS 存在的必要性。如下圖所示,假設(shè) TCP 協(xié)議中不存在 MSS 的概念,因為每個數(shù)據(jù)段的大小沒有上限,當 TCP 協(xié)議交給 IP 層發(fā)送兩個 1600 字節(jié)(包括 IP 和 TCP 協(xié)議頭)的數(shù)據(jù)包時,由于物理設(shè)備的限制,IP 協(xié)議的路徑 MTU 為 1500 字節(jié),所以 IP 協(xié)議會對數(shù)據(jù)包分片:
圖 4 - 分片傳輸?shù)?TCP 數(shù)據(jù)
四個數(shù)據(jù)包中只有兩個會包含 TCP 協(xié)議頭,即控制位、序列號等信息,剩下的兩個數(shù)據(jù)包中不包含任何信息。因為網(wǎng)絡(luò)無法保證數(shù)據(jù)包的送達順序,所以當上述四個數(shù)據(jù)包亂序到達目的主機時,因為數(shù)據(jù)包中 TCP 協(xié)議頭的缺失,所以接收方?jīng)]有辦法對數(shù)據(jù)包進行重組,自然也就無法保證可靠性和順序了。
總結(jié)
數(shù)據(jù)拆分的根本原因說到底還是物理設(shè)備的限制,不過每一層協(xié)議都受限于下一層協(xié)議做出的決定,并依賴下層協(xié)議重新決定設(shè)計和實現(xiàn)的方法。雖然 TCP/IP 協(xié)議在傳輸數(shù)據(jù)時都需要對數(shù)據(jù)進行拆分,但是它們做出拆分數(shù)據(jù)的設(shè)計基于不同的上下文,也有著不同的目的,我們在這里總結(jié)一下兩個網(wǎng)絡(luò)協(xié)議做出類似決定的原因:
- IP 協(xié)議拆分數(shù)據(jù)是因為物理設(shè)備的限制,一次能夠傳輸?shù)臄?shù)據(jù)由路徑上 MTU 最小的設(shè)備決定,一旦 IP 協(xié)議傳輸?shù)臄?shù)據(jù)包超過 MTU 的限制就會發(fā)生丟包,所以我們需要通過路徑 MTU 發(fā)現(xiàn)獲取傳輸路徑上的 MTU 限制;
- TCP 協(xié)議拆分數(shù)據(jù)是為了保證傳輸?shù)目煽啃院晚樞颍鳛榭煽康膫鬏攨f(xié)議,為了保證數(shù)據(jù)的傳輸順序,它需要為每一個數(shù)據(jù)段增加包含序列號的 TCP 協(xié)議頭,如果數(shù)據(jù)段大小超過了 IP 協(xié)議的 MTU 限制,接收方就無法按順序?qū)?shù)據(jù)包進行重組,TCP 協(xié)議也就無法提供可靠性和順序的保證;
通過本文的分析,相信各位讀者不僅了解了為什么 TCP/IP 協(xié)議會拆分數(shù)據(jù),也了解了為什么 UDP 協(xié)議的數(shù)據(jù)報不應(yīng)該超過 MTU - 28 字節(jié),一旦超過該限制,IP 協(xié)議的分片機制會增加 UDP 數(shù)據(jù)報無法重組的可能性]。到最后,我們還是來看一些比較開放的相關(guān)問題,有興趣的讀者可以仔細思考一下下面的問題:
- IP 協(xié)議的分片機制都會導(dǎo)致哪些問題?
- TCP 連接雙方是如何確定連接的 MSS?這個值是動態(tài)的么?