從字面上來看,很多人會認(rèn)為 TCP/IP 是 TCP、IP 這兩種協(xié)議,實際上TCP/IP 協(xié)議族指的是在 IP 協(xié)議通信過程中用到的協(xié)議的統(tǒng)稱
前言
可以看到協(xié)議的分層從上往下依次是
- Ethernet II:網(wǎng)絡(luò)接口層以太網(wǎng)幀頭部信息
- Internet Protocol Version 4:互聯(lián)網(wǎng)層 IP 包頭部信息
- Transmission Control Protocol:傳輸層的數(shù)據(jù)段頭部信息,此處是 TCP 協(xié)議
- Hypertext Transfer Protocol:應(yīng)用層 HTTP 的信息
網(wǎng)絡(luò)分層
應(yīng)用層(Application Layer)
應(yīng)用層的本質(zhì)是規(guī)定了應(yīng)用程序之間如何相互傳遞報文, 以 HTTP 協(xié)議為例,它規(guī)定了:
- 報文的類型,是請求報文還是響應(yīng)報文
- 報文的語法,報文分為幾段,各段是什么含義、用什么分隔,每個部分的每個字段什么什么含義
- 進程應(yīng)該以什么樣的時序發(fā)送報文和處理響應(yīng)報文
HTTP 客戶端和 HTTP 服務(wù)端的首要工作就是根據(jù) HTTP 協(xié)議的標(biāo)準(zhǔn)組裝和解析 HTTP 數(shù)據(jù)包,每個 HTTP 報文格式由三部分組成:
- 起始行(start line),起始行根據(jù)是請求報文還是響應(yīng)報文分為「請求行」和「響應(yīng)行」。這個例子中起始行是GET / HTTP/1.1,表示這是一個 GET 請求,請求的 URL 為/,協(xié)議版本為HTTP 1.1,起始行最后會有一個空行CRLF(rn)與下面的首部分隔開
- 首部(header),首部采用形如key:value的方式,比如常見的User-Agent、ETag、Content-Length都屬于 HTTP 首部,每個首部直接也是用空行分隔
- 可選的實體(entity),實體是 HTTP 真正要傳輸?shù)膬?nèi)容,比如下載一個圖片文件,傳輸?shù)囊欢?HTML等
以本例的請求報文格式為例:
除了我們熟知的 HTTP 協(xié)議,還有下面這些非常常用的應(yīng)用層協(xié)議
- 域名解析協(xié)議 DNS
- 收發(fā)郵件 SMTP 和 POP3 協(xié)議
- 時鐘同步協(xié)議 NTP
- 網(wǎng)絡(luò)文件共享協(xié)議 NFS
傳輸層(Transport Layer)
傳輸層的作用是為兩臺主機之間的「應(yīng)用進程」提供端到端的邏輯通信,相隔幾千公里的兩臺主機的進程就好像在直接通信一樣。
雖然是叫傳輸層,但是并不是將數(shù)據(jù)包從一臺主機傳送到另一臺,而是對傳輸行為進行控制,這本小冊介紹的主要內(nèi)容 TCP 協(xié)議就被稱為傳輸控制協(xié)議(Transmission Control Protocol),為下面兩層協(xié)議提供數(shù)據(jù)包的重傳、流量控制、擁塞控制等。
假設(shè)你正在電腦上用微信跟女朋友聊天,用 QQ 跟技術(shù)大佬們討論技術(shù)細(xì)節(jié),當(dāng)電腦收到一個數(shù)據(jù)包時,它怎么知道這是一條微信的聊天內(nèi)容,還是一條 QQ 的消息呢?
這就是端口號的作用。傳輸層用端口號來標(biāo)識不同的應(yīng)用程序,主機收到數(shù)據(jù)包以后根據(jù)目標(biāo)端口號將數(shù)據(jù)包傳遞給對應(yīng)的應(yīng)用程序進行處理。比如這個例子中,目標(biāo)端口號為 80,百度的服務(wù)器就根據(jù)這個目標(biāo)端口號將請求交給監(jiān)聽 80 端口的應(yīng)用程序(可能是 Nginx 等負(fù)載均衡器)處理。
網(wǎng)絡(luò)互連層(Internet Layer)
網(wǎng)絡(luò)互連層提供了主機到主機的通信,將傳輸層產(chǎn)生的的數(shù)據(jù)包封裝成分組數(shù)據(jù)包發(fā)送到目標(biāo)主機,并提供路由選擇的能力。
IP 協(xié)議是網(wǎng)絡(luò)層的主要協(xié)議,TCP 和 UDP 都是用 IP 協(xié)議作為網(wǎng)絡(luò)層協(xié)議。這一層的主要作用是給包加上源地址和目標(biāo)地址,將數(shù)據(jù)包傳送到目標(biāo)地址。
IP 協(xié)議是一個無連接的協(xié)議,也不具備重發(fā)機制,這也是 TCP 協(xié)議復(fù)雜的原因之一就是基于了這樣一個「不靠譜」的協(xié)議。
網(wǎng)絡(luò)訪問層(Network Access Layer)
網(wǎng)絡(luò)訪問層也有說法叫做網(wǎng)絡(luò)接口層,以太網(wǎng)、Wifi、藍(lán)牙工作在這一層,網(wǎng)絡(luò)訪問層提供了主機連接到物理網(wǎng)絡(luò)需要的硬件和相關(guān)的協(xié)議。這一層我們不做重點討論。
分層的好處是什么呢?
分層的本質(zhì)是通過分離關(guān)注點而讓復(fù)雜問題簡單化,通過分層可以做到:
- 各層獨立:限制了依賴關(guān)系的范圍,各層之間使用標(biāo)準(zhǔn)化的接口,各層不需要知道上下層是如何工作的,增加或者修改一個應(yīng)用層協(xié)議不會影響傳輸層協(xié)議
- 靈活性更好:比如路由器不需要應(yīng)用層和傳輸層,分層以后路由器就可以只用加載更少的幾個協(xié)議層
- 易于測試和維護:提高了可測試性,可以獨立的測試特定層,某一層有了更好的實現(xiàn)可以整體替換掉
- 能促進標(biāo)準(zhǔn)化:每一層職責(zé)清楚,方便進行標(biāo)準(zhǔn)化
TCP概述-可靠的、面向連接的、基于字節(jié)流、全雙工的協(xié)議
TCP 是面向連接的協(xié)議
面向連接(connection-oriented):面向連接的協(xié)議要求正式發(fā)送數(shù)據(jù)之前需要通過「握手」建立一個邏輯連接,結(jié)束通信時也是通過有序的四次揮手來斷開連接。
無連接(connectionless):無連接的協(xié)議則不需要
三次握手
通過三次握手協(xié)商好雙方后續(xù)通信的起始序列號、窗口縮放大小等信息。
TCP 協(xié)議是可靠的
IP 是一種無連接、不可靠的協(xié)議:它盡最大可能將數(shù)據(jù)報從發(fā)送者傳輸給接收者,但并不保證包到達(dá)的順序會與它們被傳輸?shù)捻樞蛞恢拢膊槐WC包是否重復(fù),甚至都不保證包是否會達(dá)到接收者。不保證有序、去重、完整。
TCP 要想在 IP 基礎(chǔ)上構(gòu)建可靠的傳輸層協(xié)議,必須有一個復(fù)雜的機制來保障可靠性。主要有下面幾個方面:
- 對每個包提供校驗和
- 包的序列號解決了接收數(shù)據(jù)的亂序、重復(fù)問題
- 超時重傳
- 流量控制、擁塞控制
校驗和(checksum) 每個 TCP 包首部中都有兩字節(jié)用來表示校驗和,防止在傳輸過程中有損壞。如果收到一個校驗和有差錯的報文,TCP 不會發(fā)送任何確認(rèn)直接丟棄它,等待發(fā)送端重傳。
包的序列號保證了接收數(shù)據(jù)的亂序和重復(fù)問題假設(shè)我們往 TCP 套接字里寫 3000 字節(jié)的數(shù)據(jù)導(dǎo)致 TCP發(fā)送了 3 個數(shù)據(jù)包,每個數(shù)據(jù)包大小為 1000 字節(jié):第一個包序列號為[1~1001),第二個包序列號為 [10012001),第三個包序號為[20013001)
假如因為網(wǎng)絡(luò)的原因?qū)е碌诙€、第三個包先到接收端,第一個包最后才到,接收端也不會因為他們到達(dá)的順序不一致把包弄錯,TCP 會根據(jù)他們的序號進行重新的排列然后把結(jié)果傳遞給上層應(yīng)用程序。
如果 TCP 接收到重復(fù)的數(shù)據(jù),可能的原因是超時重傳了兩次但這個包并沒有丟失,接收端會收到兩次同樣的數(shù)據(jù),它能夠根據(jù)包序號丟棄重復(fù)的數(shù)據(jù)。
超時重傳 TCP 發(fā)送數(shù)據(jù)后會啟動一個定時器,等待對端確認(rèn)收到這個數(shù)據(jù)包。如果在指定的時間內(nèi)沒有收到 ACK 確認(rèn),就會重傳數(shù)據(jù)包,然后等待更長時間,如果還沒有收到就再重傳,在多次重傳仍然失敗以后,TCP 會放棄這個包。后面我們講到超時重傳模塊的時候會詳細(xì)介紹這部分內(nèi)容。
TCP 是面向字節(jié)流的協(xié)議
TCP 是一種字節(jié)流(byte-stream)協(xié)議,流的含義是沒有固定的報文邊界。
假設(shè)你調(diào)用 2 次 write 函數(shù)往 socket 里依次寫 500 字節(jié)、800 字節(jié)。write 函數(shù)只是把字節(jié)拷貝到內(nèi)核緩沖區(qū),最終會以多少條報文發(fā)送出去是不確定的,如下圖所示
上面出現(xiàn)的情況取決于諸多因素:路徑最大傳輸單元 MTU、發(fā)送窗口大小、擁塞窗口大小等。
當(dāng)接收方從 TCP 套接字讀數(shù)據(jù)時,它是沒法得知對方每次寫入的字節(jié)是多少的。接收端可能分2 次每次 650 字節(jié)讀取,也有可能先分三次,一次 100 字節(jié),一次 200 字節(jié),一次 1000 字節(jié)進行讀取。
TCP 是全雙工的協(xié)議
在 TCP 中發(fā)送端和接收端可以是客戶端/服務(wù)端,也可以是服務(wù)器/客戶端,通信的雙方在任意時刻既可以是接收數(shù)據(jù)也可以是發(fā)送數(shù)據(jù),每個方向的數(shù)據(jù)流都獨立管理序列號、滑動窗口大小、MSS 等信息。
在 TCP 中發(fā)送端和接收端可以是客戶端/服務(wù)端,也可以是服務(wù)器/客戶端,通信的雙方在任意時刻既可以是接收數(shù)據(jù)也可以是發(fā)送數(shù)據(jù),每個方向的數(shù)據(jù)流都獨立管理序列號、滑動窗口大小、MSS 等信息。
小結(jié)與思考
TCP 是一個可靠的(reliable)、面向連接的(connection-oriented)、基于字節(jié)流(byte-stream)、全雙工(full-duplex)的協(xié)議。發(fā)送端在發(fā)送數(shù)據(jù)以后啟動一個定時器,如果超時沒有收到對端確認(rèn)會進行重傳,接收端利用序列號對收到的包進行排序、丟棄重復(fù)數(shù)據(jù),TCP 還提供了流量控制、擁塞控制等機制保證了穩(wěn)定性。
TCP提供了一種字節(jié)流服務(wù),而收發(fā)雙方都不保持記錄的邊界,應(yīng)用程序應(yīng)該如何提供他們自己的記錄標(biāo)識呢?
答:除了U D P的檢驗和,其他都是必需的。I P檢驗和只覆蓋了 I P首部,而其他字段都緊接著I P首部開始。
17.2 為什么我們已經(jīng)討論的所有 I n t e r n e t協(xié)議( I P, ICMP, IGMP, UDP, TCP)收到有檢驗和錯的分組都僅作丟棄處理?
答:源I P地址、源端口號或者協(xié)議字段可能被破壞了。
17.3 T C P提供了一種字節(jié)流服務(wù),而收發(fā)雙方都不保持記錄的邊界。應(yīng)用程序如何提供它們
自己的記錄標(biāo)識?
答:很多I n t e r n e t應(yīng)用使用一個回車和換行來標(biāo)記每個應(yīng)用記錄的結(jié)束。這是 NVT ASCII采用的編碼( 2 6 . 4節(jié)) 。另外一種技術(shù)是在每個記錄之前加上一個記錄的字節(jié)計數(shù), D N S(習(xí)題1 4 . 4)和Sun RPC( 2 9 . 2節(jié))采用了這種技術(shù)。
17.4 為什么在T C P首部的開始便是源和目的的端口號?
答:就像我們在6 . 5節(jié)所看到的,一個I C M P差錯報文必須至少返回引起差錯的 I P數(shù)據(jù)報中除了I P首部的前8 個字節(jié)。當(dāng)T C P收到一個I C M P差錯報文時,它需要檢查兩個端口號以決定差錯對應(yīng)于哪個連接。因此,端口號必須包含在T C P首部的前8個字節(jié)里。
17.5 為什么T C P首部有一個首部長度字段而 U D P首部(圖11 - 2)中卻沒有?
TCP首部的最后有一些選項,但 U D P首部中沒有選項。
packetdrill-google協(xié)議棧測試神器-TODO
以 centos7 為例
- 首先從 github 上 clone 最新的源碼 github.com/google/pack…
- 進入源碼目錄cd gtests/net/packetdrill
- 安裝 bison和 flex 庫:sudo yum install -y bison flex
- 為避免 offload 機制對包大小的影響,修改 netdev.c 注釋掉 set_device_offload_flags 函數(shù)所有內(nèi)容
- 執(zhí)行 ./configure
- 修改 Makefile,去掉第一行的末尾的 -static
- 執(zhí)行 make 命令編譯
- 確認(rèn)編譯無誤地生成了 packetdrill 可執(zhí)行文件
詳解
tcp基石-剖析首部字段
這篇文章來講講 TCP 報文首部相關(guān)的概念,這些頭部是支撐 TCP 復(fù)雜功能的基石。完整的 TCP 頭部如下圖所示:
我們用一次訪問百度網(wǎng)頁抓包的例子來開始。
源端口號、目標(biāo)端口號
在第一個包的詳情中,首先看到的高亮部分的源端口號(Src Port)和目標(biāo)端口號(Dst Port),這個例子中本地源端口號為 61024,百度目標(biāo)端口號是 80。
TCP 報文頭部里沒有源 ip 和目標(biāo) ip 地址,只有源端口號和目標(biāo)端口號。
這也是初學(xué) wireshark 抓包時很多人會有的一個疑問:過濾 ip 地址為 172.19.214.24 包的條件為什么不是 “tcp.addr == 172.19.214.24”,而是 “ip.addr == 172.19.214.24”
TCP 的報文里是沒有源 ip 和目標(biāo) ip 的,因為那是 IP 層協(xié)議的事情,TCP 層只有源端口和目標(biāo)端口。
源 IP、源端口、目標(biāo) IP、目標(biāo)端口構(gòu)成了 TCP 連接的「四元組」。一個四元組可以唯一標(biāo)識一個連接。
序列號(Sequence number)
TCP 是面向字節(jié)流的協(xié)議,通過 TCP 傳輸?shù)淖止?jié)流的每個字節(jié)都分配了序列號,序列號(Sequence number)指的是本報文段第一個字節(jié)的序列號。
序列號加上報文的長度,就可以確定傳輸?shù)氖悄囊欢螖?shù)據(jù)。序列號是一個 32 位的無符號整數(shù),達(dá)到 2^32-1 后循環(huán)到 0。
在 SYN 報文中,序列號用于交換彼此的初始序列號,在其它報文中,序列號用于保證包的順序。
因為網(wǎng)絡(luò)層(IP 層)不保證包的順序,TCP 協(xié)議利用序列號來解決網(wǎng)絡(luò)包亂序、重復(fù)的問題,以保證數(shù)據(jù)包以正確的順序組裝傳遞給上層應(yīng)用。
如果發(fā)送方發(fā)送的是四個報文序列號分別是1、2、3、4,但到達(dá)接收方的順序是 2、4、3、1,接收方就可以通過序列號的大小順序組裝出原始的數(shù)據(jù)。
初始序列號(Initial Sequence Number, ISN)
在建立連接之初,通信雙方都會各自選擇一個序列號,稱之為初始序列號。在建立連接時,通信雙方通過 SYN 報文交換彼此的 ISN,如下圖所示:
初始建立連接的過程中 SYN 報文交換過程如下圖所示:
其中第 2 步和第 3 步可以合并一起,這就是三次握手的過程:
初始序列號是如何生成的
__be16 sport, __be16 dport)
{
u32 hash[MD5_DIGEST_WORDS];
net_secret_init();
hash[0] = (__force u32)saddr;
hash[1] = (__force u32)daddr;
hash[2] = ((__force u16)sport << 16) + (__force u16)dport;
//一個長度為 16 的 int 數(shù)組,只有在第一次調(diào)用 net_secret_init 的時時候會將將這個數(shù)組的值初始化為隨機值。在系統(tǒng)重啟前保持不變。
hash[3] = net_secret[15];
md5_transform(hash, net_secret);
return seq_scale(hash[0]);
}
static u32 seq_scale(u32 seq)
{
return seq + (ktime_to_ns(ktime_get_real()) >> 6);
}
可以看到初始序列號的計算函數(shù) secure_tcp_sequence_number() 的邏輯是通過源地址、目標(biāo)地址、源端口、目標(biāo)端口和隨機因子通過 MD5 進行進行計算。如果僅有這幾個因子,對于四元組相同的請求,計算出的初始序列號總是相同,這必然有很大的安全風(fēng)險,所以函數(shù)的最后將計算出的序列號通過 seq_scale 函數(shù)再次計算。
seq_scale 函數(shù)加入了時間因子,對于四元組相同的連接,序列號也不會重復(fù)了。
序列號回繞了怎么處理
序列號是一個 32 位的無符號整數(shù),從前面介紹的初始序列號計算算法可以知道,ISN 并不是從 0 開始,所以同一個連接的序列號是有可能溢出回繞(sequence wraparound)的。TCP 的很多校驗比如丟包、亂序判斷都是通過比較包的序號來實現(xiàn)的,我們來看看 linux 內(nèi)核是如何處理的,代碼如下所示。
{
return (__s32)(seq1-seq2) < 0;
}
其中 __u32 表示無符號的 32 位整數(shù),__s32 表示有符號的 32 位整數(shù)。為什么 seq1 - seq2 轉(zhuǎn)為有符號的 32 位整數(shù)就可以判斷 seq1 和 seq2 的大小了呢?
以 seq1 為 0xFFFFFFFF、seq2 為 0x02(回繞)為例,它們相減的結(jié)果如下。
seq1 - seq2 = 0xFFFFFFFF - 0x02 = 0xFFFFFFFD
0xFFFFFFFD 最高位為 1,表示為負(fù)數(shù),實際值為 -(0x00000002 + 1) = -3,這樣即使 seq2 回繞了,也可以知道 seq1
確認(rèn)號
TCP 使用確認(rèn)號(Acknowledgment number, ACK)來告知對方下一個期望接收的序列號,小于此確認(rèn)號的所有字節(jié)都已經(jīng)收到。
關(guān)于確認(rèn)號有幾個注意點:
- 不是所有的包都需要確認(rèn)的
- 不是收到了數(shù)據(jù)包就立馬需要確認(rèn)的,可以延遲一會再確認(rèn)
- ACK 包本身不需要被確認(rèn),否則就會無窮無盡死循環(huán)了
- 確認(rèn)號永遠(yuǎn)是表示小于此確認(rèn)號的字節(jié)都已經(jīng)收到
TCP Flags
TCP 有很多種標(biāo)記,有些用來發(fā)起連接同步初始序列號,有些用來確認(rèn)數(shù)據(jù)包,還有些用來結(jié)束連接。TCP 定義了一個 8 位的字段用來表示 flags,大部分都只用到了后 6 個,如下圖所示
下面這個是 wireshark 第一個 SYN 包的 flags 截圖
我們通常所說的 SYN、ACK、FIN、RST 其實只是把 flags 對應(yīng)的 bit 位置為 1 而已,這些標(biāo)記可以組合使用,比如 SYN+ACK,F(xiàn)IN+ACK 等
- SYN(Synchronize):用于發(fā)起連接數(shù)據(jù)包同步雙方的初始序列號
- ACK(Acknowledge):確認(rèn)數(shù)據(jù)包
- RST(Reset):這個標(biāo)記用來強制斷開連接,通常是之前建立的連接已經(jīng)不在了、包不合法、或者實在無能為力處理
- FIN(Finish):通知對方我發(fā)完了所有數(shù)據(jù),準(zhǔn)備斷開連接,后面我不會再發(fā)數(shù)據(jù)包給你了。
- PSH(Push):告知對方這些數(shù)據(jù)包收到以后應(yīng)該馬上交給上層應(yīng)用,不能緩存起來
窗口大小
可以看到用于表示窗口大小的"Window Size" 只有 16 位,可能 TCP 協(xié)議設(shè)計者們認(rèn)為 16 位的窗口大小已經(jīng)夠用了,也就是最大窗口大小是 65535 字節(jié)(64KB)。就像網(wǎng)傳蓋茨曾經(jīng)說過:“640K內(nèi)存對于任何人來說都足夠了”一樣。
自己挖的坑當(dāng)然要自己填,因此TCP 協(xié)議引入了「TCP 窗口縮放」選項 作為窗口縮放的比例因子,比例因子值的范圍是 0 ~ 14,其中最小值 0 表示不縮放,最大值 14。比例因子可以將窗口擴大到原來的 2 的 n 次方,比如窗口大小縮放前為 1050,縮放因子為 7,則真正的窗口大小為 1050 * 128 = 134400,如下圖所示
可選項
可選項的格式入下所示
以 MSS 為例,kind=2,length=4,value=1460
常用的選項有以下幾個:
- MSS:最大段大小選項,是 TCP 允許的從對方接收的最大報文段
- SACK:選擇確認(rèn)選項
- Window Scale:窗口縮放選項
網(wǎng)絡(luò)數(shù)據(jù)包大小-MUT與MSS
前面的文章中介紹過一個應(yīng)用層的數(shù)據(jù)包會經(jīng)過傳輸層、網(wǎng)絡(luò)層的層層包裝,交給網(wǎng)絡(luò)接口層傳輸。假設(shè)上層的應(yīng)用調(diào)用 write 等函數(shù)往 socket 寫入了 10KB 的數(shù)據(jù),TCP 會如何處理呢?是直接加上 TCP 頭直接交給網(wǎng)絡(luò)層嗎?這篇文章我們來講講這相關(guān)的知識
MUT
數(shù)據(jù)鏈路層傳輸?shù)膸笮∈怯邢拗频模荒馨岩粋€太大的包直接塞給鏈路層,這個限制被稱為「最大傳輸單元(Maximum Transmission Unit, MTU)」
下圖是以太網(wǎng)的幀格式,以太網(wǎng)的幀最小的幀是 64 字節(jié),除去 14 字節(jié)頭部和 4 字節(jié) CRC 字段,有效荷載最小為 46 字節(jié)。最大的幀是 1518 字節(jié),除去 14 字節(jié)頭部和 4 字節(jié) CRC,有效荷載最大為 1500,這個值就是以太網(wǎng)的 MTU。因此如果傳輸 100KB 的數(shù)據(jù),至少需要 (100 * 1024 / 1500) = 69 個以太網(wǎng)幀。
不同的數(shù)據(jù)鏈路層的 MTU 是不同的。通過netstat -i 可以查看網(wǎng)卡的 mtu,比如在 我的 centos 機器上可以看到
IP分段
IPv4 數(shù)據(jù)報的最大大小為 65535 字節(jié),這已經(jīng)遠(yuǎn)遠(yuǎn)超過了以太網(wǎng)的 MTU,而且有些網(wǎng)絡(luò)還會開啟巨幀(Jumbo Frame)能達(dá)到 9000 字節(jié)。當(dāng)一個 IP 數(shù)據(jù)包大于 MTU 時,IP 會把數(shù)據(jù)報文進行切割為多個小的片段(小于 MTU),使得這些小的報文可以通過鏈路層進行傳輸。
IP 頭部中有一個表示分片偏移量的字段,用來表示該分段在原始數(shù)據(jù)報文中的位置,如下圖所示
前面我們提到 IP 協(xié)議不會對丟包進行重傳,那么 IP 分段中有分片丟失、損壞的話,會發(fā)生什么呢?這種情況下,目標(biāo)主機將沒有辦法將分段的數(shù)據(jù)包重組為一個完整的數(shù)據(jù)包,依賴于傳輸層是否進行重傳。
利用 IP 包分片的策略,有一種對應(yīng)的網(wǎng)絡(luò)攻擊方式IP fragment attack,就是一直傳More fragments = 1的包,導(dǎo)致接收方一直緩存分片,從而可能導(dǎo)致接收方內(nèi)存耗盡。
因為有 MTU 的存在,TCP 每次發(fā)包的大小也限制了,這就是下面要介紹的 MSS。
MSS
TCP 為了避免被發(fā)送方分片,會主動把數(shù)據(jù)分割成小段再交給網(wǎng)絡(luò)層,最大的分段大小稱之為 MSS(Max Segment Size)。
MSS = MTU - IP header頭大小 - TCP 頭大小
這樣一個 MSS 的數(shù)據(jù)恰好能裝進一個 MTU 而不用分片。在以太網(wǎng)中 TCP 的 MSS = 1500(MTU) - 20(IP 頭大小) - 20(TCP 頭大小)= 1460。
為什么有時候抓包看到的單個數(shù)據(jù)包大于 MTU
這就要說到 TSO(TCP Segment Offload)特性了,TSO 特性是指由網(wǎng)卡代替 CPU 實現(xiàn) packet 的分段和合并,節(jié)省系統(tǒng)資源,因此 TCP 可以抓到超過 MTU 的包,但是不是真正傳輸?shù)膯蝹€包會超過鏈路的 MTU。
-
通信
+關(guān)注
關(guān)注
18文章
6024瀏覽量
135950 -
互聯(lián)網(wǎng)
+關(guān)注
關(guān)注
54文章
11148瀏覽量
103237 -
IP
+關(guān)注
關(guān)注
5文章
1701瀏覽量
149507 -
TCP協(xié)議
+關(guān)注
關(guān)注
1文章
91瀏覽量
12070
發(fā)布評論請先 登錄
相關(guān)推薦
評論