當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]每日一句英語(yǔ)學(xué)習(xí),每天進(jìn)步一點(diǎn)點(diǎn): 前言 “哈?啥是大白鯊?” 咳咳,主要是因?yàn)榫W(wǎng)絡(luò)分析工具 Wireshark 的圖標(biāo)特別像大白鯊頂部的角。 不信你看: Wireshark “為什么拖了怎么久才發(fā)文?” 為了讓大家更容易「看得見(jiàn)」 TCP,我搭建不少測(cè)試環(huán)境,并且數(shù)據(jù)

每日一句英語(yǔ)學(xué)習(xí),每天進(jìn)步一點(diǎn)點(diǎn):
實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

前言

“哈?啥是大白鯊?”

咳咳,主要是因?yàn)榫W(wǎng)絡(luò)分析工具 Wireshark 的圖標(biāo)特別像大白鯊頂部的角。

不信你看:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
Wireshark

“為什么拖了怎么久才發(fā)文?”

為了讓大家更容易「看得見(jiàn)」 TCP,我搭建不少測(cè)試環(huán)境,并且數(shù)據(jù)包抓很多次,花費(fèi)了不少時(shí)間,才抓到比較容易分析的數(shù)據(jù)包。

接下來(lái)丟包、亂序、超時(shí)重傳、快速重傳、選擇性確認(rèn)、流量控制等等 TCP 的特性,都能「一覽無(wú)云」。

沒(méi)錯(cuò),我把 TCP 的"衣服扒光"了,就為了給大家看的清楚,嘻嘻。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
提綱

正文

顯形“不可見(jiàn)”的網(wǎng)絡(luò)包

網(wǎng)絡(luò)世界中的數(shù)據(jù)包交互我們?nèi)庋凼强床灰?jiàn)的,它們就好像隱形了一樣,我們對(duì)著課本學(xué)習(xí)計(jì)算機(jī)網(wǎng)絡(luò)的時(shí)候就會(huì)覺(jué)得非常的抽象,加大了學(xué)習(xí)的難度。

還別說(shuō),我自己在大學(xué)的時(shí)候,也是如此。

直到工作后,認(rèn)識(shí)了兩大分析網(wǎng)絡(luò)的利器:tcpdump 和 Wireshark,這兩大利器把我們“看不見(jiàn)”的數(shù)據(jù)包,呈現(xiàn)在我們眼前,一目了然。

唉,當(dāng)初大學(xué)學(xué)習(xí)計(jì)網(wǎng)的時(shí)候,要是能知道這兩個(gè)工具,就不會(huì)學(xué)的一臉懵逼。

tcpdump 和 Wireshark 有什么區(qū)別?

tcpdump 和 Wireshark 就是最常用的網(wǎng)絡(luò)抓包和分析工具,更是分析網(wǎng)絡(luò)性能必不可少的利器。

  • tcpdump 僅支持命令行格式使用,常用在 Linux 服務(wù)器中抓取和分析網(wǎng)絡(luò)包。

  • Wireshark 除了可以抓包外,還提供了可視化分析網(wǎng)絡(luò)包的圖形頁(yè)面。

所以,這兩者實(shí)際上是搭配使用的,先用 tcpdump 命令在 Linux 服務(wù)器上抓包,接著把抓包的文件拖出到 Windows 電腦后,用 Wireshark 可視化分析。

當(dāng)然,如果你是在 Windows 上抓包,只需要用 Wireshark 工具就可以。

tcpdump 在 Linux 下如何抓包?

tcpdump 提供了大量的選項(xiàng)以及各式各樣的過(guò)濾表達(dá)式,來(lái)幫助你抓取指定的數(shù)據(jù)包,不過(guò)不要擔(dān)心,只需要掌握一些常用選項(xiàng)和過(guò)濾表達(dá)式,就可以滿足大部分場(chǎng)景的需要了。

假設(shè)我們要抓取下面的 ping 的數(shù)據(jù)包:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

要抓取上面的 ping 命令數(shù)據(jù)包,首先我們要知道 ping 的數(shù)據(jù)包是 icmp 協(xié)議,接著在使用 tcpdump 抓包的時(shí)候,就可以指定只抓 icmp 協(xié)議的數(shù)據(jù)包:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

那么當(dāng) tcpdump 抓取到 icmp 數(shù)據(jù)包后, 輸出格式如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

從 tcpdump 抓取的 icmp 數(shù)據(jù)包,我們很清楚的看到 icmp echo 的交互過(guò)程了,首先發(fā)送方發(fā)起了 ICMP echo request 請(qǐng)求報(bào)文,接收方收到后回了一個(gè) ICMP echo reply 響應(yīng)報(bào)文,之后 seq 是遞增的。

我在這里也幫你整理了一些最常見(jiàn)的用法,并且繪制成了表格,你可以參考使用。

首先,先來(lái)看看常用的選項(xiàng)類,在上面的 ping 例子中,我們用過(guò) -i 選項(xiàng)指定網(wǎng)口,用過(guò) -nn 選項(xiàng)不對(duì) IP 地址和端口名稱解析。其他常用的選項(xiàng),如下表格:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
tcpdump 常用選項(xiàng)類

接下來(lái),我們?cè)賮?lái)看看常用的過(guò)濾表用法,在上面的 ping 例子中,我們用過(guò)的是 icmp and host 183.232.231.174,表示抓取 icmp 協(xié)議的數(shù)據(jù)包,以及源地址或目標(biāo)地址為 183.232.231.174 的包。其他常用的過(guò)濾選項(xiàng),我也整理成了下面這個(gè)表格。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
tcpdump 常用過(guò)濾表達(dá)式類

說(shuō)了這么多,你應(yīng)該也發(fā)現(xiàn)了,tcpdump 雖然功能強(qiáng)大,但是輸出的格式并不直觀。

所以,在工作中 tcpdump 只是用來(lái)抓取數(shù)據(jù)包,不用來(lái)分析數(shù)據(jù)包,而是把 tcpdump 抓取的數(shù)據(jù)包保存成 pcap 后綴的文件,接著用 Wireshark 工具進(jìn)行數(shù)據(jù)包分析。

Wireshark 工具如何分析數(shù)據(jù)包?

Wireshark 除了可以抓包外,還提供了可視化分析網(wǎng)絡(luò)包的圖形頁(yè)面,同時(shí),還內(nèi)置了一系列的匯總分析工具。

比如,拿上面的 ping 例子來(lái)說(shuō),我們可以使用下面的命令,把抓取的數(shù)據(jù)包保存到 ping.pcap 文件

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

接著把 ping.pcap 文件拖到電腦,再用 Wireshark 打開(kāi)它。打開(kāi)后,你就可以看到下面這個(gè)界面:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

是吧?在 Wireshark 的頁(yè)面里,可以更加直觀的分析數(shù)據(jù)包,不僅展示各個(gè)網(wǎng)絡(luò)包的頭部信息,還會(huì)用不同的顏色來(lái)區(qū)分不同的協(xié)議,由于這次抓包只有 ICMP 協(xié)議,所以只有紫色的條目。

接著,在網(wǎng)絡(luò)包列表中選擇某一個(gè)網(wǎng)絡(luò)包后,在其下面的網(wǎng)絡(luò)包詳情中,可以更清楚的看到,這個(gè)網(wǎng)絡(luò)包在協(xié)議棧各層的詳細(xì)信息。比如,以編號(hào) 1 的網(wǎng)絡(luò)包為例子:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
ping 網(wǎng)絡(luò)包

  • 可以在數(shù)據(jù)鏈路層,看到 MAC 包頭信息,如源 MAC 地址和目標(biāo) MAC 地址等字段;

  • 可以在 IP 層,看到 IP 包頭信息,如源 IP 地址和目標(biāo) IP 地址、TTL、IP 包長(zhǎng)度、協(xié)議等 IP 協(xié)議各個(gè)字段的數(shù)值和含義;

  • 可以在 ICMP 層,看到 ICMP 包頭信息,比如 Type、Code 等 ICMP 協(xié)議各個(gè)字段的數(shù)值和含義;

Wireshark 用了分層的方式,展示了各個(gè)層的包頭信息,把“不可見(jiàn)”的數(shù)據(jù)包,清清楚楚的展示了給我們,還有理由學(xué)不好計(jì)算機(jī)網(wǎng)絡(luò)嗎?是不是相見(jiàn)恨晚

從 ping 的例子中,我們可以看到網(wǎng)絡(luò)分層就像有序的分工,每一層都有自己的責(zé)任范圍和信息,上層協(xié)議完成工作后就交給下一層,最終形成一個(gè)完整的網(wǎng)絡(luò)包。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

解密 TCP 三次握手和四次揮手

既然學(xué)會(huì)了 tcpdump 和 Wireshark 兩大網(wǎng)絡(luò)分析利器,那我們快馬加鞭,接下用它倆抓取和分析 HTTP 協(xié)議網(wǎng)絡(luò)包,并理解 TCP 三次握手和四次揮手的工作原理。

本次例子,我們將要訪問(wèn)的 http://192.168.3.200 服務(wù)端。在終端一用 tcpdump 命令抓取數(shù)據(jù)包:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

接著,在終端二執(zhí)行下面的 curl 命令:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

最后,回到終端一,按下 Ctrl+C 停止 tcpdump,并把得到的 http.pcap 取出到電腦。

使用 Wireshark 打開(kāi) http.pcap 后,你就可以在 Wireshark 中,看到如下的界面:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
HTTP 網(wǎng)絡(luò)包

我們都知道 HTTP 是基于 TCP 協(xié)議進(jìn)行傳輸?shù)?,那么?/p>

  • 最開(kāi)始的 3 個(gè)包就是 TCP 三次握手建立連接的包

  • 中間是 HTTP 請(qǐng)求和響應(yīng)的包

  • 而最后的 3 個(gè)包則是 TCP 斷開(kāi)連接的揮手包

Wireshark 可以用時(shí)序圖的方式顯示數(shù)據(jù)包交互的過(guò)程,從菜單欄中,點(diǎn)擊 統(tǒng)計(jì) (Statistics) -> 流量圖 (Flow Graph),然后,在彈出的界面中的「流量類型」選擇 「TCP Flows」,你可以更清晰的看到,整個(gè)過(guò)程中 TCP 流的執(zhí)行過(guò)程:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP 流量圖

你可能會(huì)好奇,為什么三次握手連接過(guò)程的 Seq 是 0 ?

實(shí)際上是因?yàn)?Wireshark 工具幫我們做了優(yōu)化,它默認(rèn)顯示的是序列號(hào) seq 是相對(duì)值,而不是真實(shí)值。

如果你想看到實(shí)際的序列號(hào)的值,可以右鍵菜單, 然后找到「協(xié)議首選項(xiàng)」,接著找到「Relative Seq」后,把它給取消,操作如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
取消序列號(hào)相對(duì)值顯示

取消后,Seq 顯示的就是真實(shí)值了:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP 流量圖

可見(jiàn),客戶端和服務(wù)端的序列號(hào)實(shí)際上是不同的,序列號(hào)是一個(gè)隨機(jī)值。

這其實(shí)跟我們書上看到的 TCP 三次握手和四次揮手很類似,作為對(duì)比,你通??吹降?TCP 三次握手和四次揮手的流程,基本是這樣的:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP 三次握手和四次揮手的流程

為什么抓到的 TCP 揮手是三次,而不是書上說(shuō)的四次?

因?yàn)榉?wù)器端收到客戶端的 FIN 后,服務(wù)器端同時(shí)也要關(guān)閉連接,這樣就可以把 ACKFIN 合并到一起發(fā)送,節(jié)省了一個(gè)包,變成了“三次揮手”。

而通常情況下,服務(wù)器端收到客戶端的 FIN 后,很可能還沒(méi)發(fā)送完數(shù)據(jù),所以就會(huì)先回復(fù)客戶端一個(gè) ACK 包,稍等一會(huì)兒,完成所有數(shù)據(jù)包的發(fā)送后,才會(huì)發(fā)送 FIN 包,這也就是四次揮手了。

如下圖,就是四次揮手的過(guò)程:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
四次揮手

TCP 三次握手異常情況實(shí)戰(zhàn)分析

TCP 三次握手的過(guò)程相信大家都背的滾瓜爛熟,那么你有沒(méi)有想過(guò)這三個(gè)異常情況:

  • TCP 第一次握手的 SYN 丟包了,會(huì)發(fā)生了什么?

  • TCP 第二次握手的 SYN、ACK 丟包了,會(huì)發(fā)生什么?

  • TCP 第三次握手的 ACK 包丟了,會(huì)發(fā)生什么?

有的小伙伴可能說(shuō):“很簡(jiǎn)單呀,包丟了就會(huì)重傳嘛?!?/p>

那我在繼續(xù)問(wèn)你:

  • 那會(huì)重傳幾次?

  • 超時(shí)重傳的時(shí)間 RTO 會(huì)如何變化?

  • 在 Linux 下如何設(shè)置重傳次數(shù)?

  • ….

是不是啞口無(wú)言,無(wú)法回答?

不知道沒(méi)關(guān)系,接下里我用三個(gè)實(shí)驗(yàn)案例,帶大家一起探究探究這三種異常。

實(shí)驗(yàn)場(chǎng)景

本次實(shí)驗(yàn)用了兩臺(tái)虛擬機(jī),一臺(tái)作為服務(wù)端,一臺(tái)作為客戶端,它們的關(guān)系如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
實(shí)驗(yàn)環(huán)境

  • 客戶端和服務(wù)端都是 CentOs 6.5 Linux,Linux 內(nèi)核版本 2.6.32

  • 服務(wù)端 192.168.12.36,apache web 服務(wù)

  • 客戶端 192.168.12.37

實(shí)驗(yàn)一:TCP 第一次握手 SYN 丟包

為了模擬 TCP 第一次握手 SYN 丟包的情況,我是在拔掉服務(wù)器的網(wǎng)線后,立刻在客戶端執(zhí)行 curl 命令:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

其間 tcpdump 抓包的命令如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

過(guò)了一會(huì), curl 返回了超時(shí)連接的錯(cuò)誤:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

date 返回的時(shí)間,可以發(fā)現(xiàn)在超時(shí)接近 1 分鐘的時(shí)間后,curl 返回了錯(cuò)誤。

接著,把 tcp_sys_timeout.pcap 文件用 Wireshark 打開(kāi)分析,顯示如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
SYN 超時(shí)重傳五次

從上圖可以發(fā)現(xiàn), 客戶端發(fā)起了 SYN 包后,一直沒(méi)有收到服務(wù)端的 ACK ,所以一直超時(shí)重傳了 5 次,并且每次 RTO 超時(shí)時(shí)間是不同的:

  • 第一次是在 1 秒超時(shí)重傳

  • 第二次是在 3 秒超時(shí)重傳

  • 第三次是在 7 秒超時(shí)重傳

  • 第四次是在 15 秒超時(shí)重傳

  • 第五次是在 31 秒超時(shí)重傳

可以發(fā)現(xiàn),每次超時(shí)時(shí)間 RTO 是指數(shù)(翻倍)上漲的,當(dāng)超過(guò)最大重傳次數(shù)后,客戶端不再發(fā)送 SYN 包。

在 Linux 中,第一次握手的 SYN 超時(shí)重傳次數(shù),是如下內(nèi)核參數(shù)指定的:

$ cat /proc/sys/net/ipv4/tcp_syn_retries
5

tcp_syn_retries 默認(rèn)值為 5,也就是 SYN 最大重傳次數(shù)是 5 次。

接下來(lái),我們繼續(xù)做實(shí)驗(yàn),把 tcp_syn_retries 設(shè)置為 2 次:

echo 2 > /proc/sys/net/ipv4/tcp_syn_retries

重傳抓包后,用 Wireshark 打開(kāi)分析,顯示如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
SYN 超時(shí)重傳兩次

實(shí)驗(yàn)一的實(shí)驗(yàn)小結(jié)

通過(guò)實(shí)驗(yàn)一的實(shí)驗(yàn)結(jié)果,我們可以得知,當(dāng)客戶端發(fā)起的 TCP 第一次握手 SYN 包,在超時(shí)時(shí)間內(nèi)沒(méi)收到服務(wù)端的 ACK,就會(huì)在超時(shí)重傳 SYN 數(shù)據(jù)包,每次超時(shí)重傳的 RTO 是翻倍上漲的,直到 SYN 包的重傳次數(shù)到達(dá) tcp_syn_retries 值后,客戶端不再發(fā)送 SYN 包。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
SYN 超時(shí)重傳

實(shí)驗(yàn)二:TCP 第二次握手 SYN、ACK 丟包

為了模擬客戶端收不到服務(wù)端第二次握手 SYN、ACK 包,我的做法是在客戶端加上防火墻限制,直接粗暴的把來(lái)自服務(wù)端的數(shù)據(jù)都丟棄,防火墻的配置如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

接著,在客戶端執(zhí)行 curl 命令:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

date 返回的時(shí)間前后,可以算出大概 1 分鐘后,curl 報(bào)錯(cuò)退出了。

客戶端在這其間抓取的數(shù)據(jù)包,用 Wireshark 打開(kāi)分析,顯示的時(shí)序圖如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

從圖中可以發(fā)現(xiàn):

  • 客戶端發(fā)起 SYN 后,由于防火墻屏蔽了服務(wù)端的所有數(shù)據(jù)包,所以 curl 是無(wú)法收到服務(wù)端的 SYN、ACK 包,當(dāng)發(fā)生超時(shí)后,就會(huì)重傳 SYN 包

  • 服務(wù)端收到客戶的 SYN 包后,就會(huì)回 SYN、ACK 包,但是客戶端一直沒(méi)有回 ACK,服務(wù)端在超時(shí)后,重傳了 SYN、ACK 包,接著一會(huì),客戶端超時(shí)重傳的 SYN 包又抵達(dá)了服務(wù)端,服務(wù)端收到后,超時(shí)定時(shí)器就重新計(jì)時(shí),然后回了 SYN、ACK 包,所以相當(dāng)于服務(wù)端的超時(shí)定時(shí)器只觸發(fā)了一次,又被重置了。

  • 最后,客戶端 SYN 超時(shí)重傳次數(shù)達(dá)到了 5 次(tcp_syn_retries 默認(rèn)值 5 次),就不再繼續(xù)發(fā)送 SYN 包了。

所以,我們可以發(fā)現(xiàn),當(dāng)?shù)诙挝帐值?SYN、ACK 丟包時(shí),客戶端會(huì)超時(shí)重發(fā) SYN 包,服務(wù)端也會(huì)超時(shí)重傳 SYN、ACK 包。

咦?客戶端設(shè)置了防火墻,屏蔽了服務(wù)端的網(wǎng)絡(luò)包,為什么 tcpdump 還能抓到服務(wù)端的網(wǎng)絡(luò)包?

添加 iptables 限制后, tcpdump 是否能抓到包 ,這要看添加的 iptables 限制條件:

  • 如果添加的是 INPUT 規(guī)則,則可以抓得到包

  • 如果添加的是 OUTPUT 規(guī)則,則抓不到包

網(wǎng)絡(luò)包進(jìn)入主機(jī)后的順序如下:

  • 進(jìn)來(lái)的順序 Wire -> NIC -> tcpdump -> netfilter/iptables

  • 出去的順序 iptables -> tcpdump -> NIC -> Wire


tcp_syn_retries 是限制 SYN 重傳次數(shù),那第二次握手 SYN、ACK 限制最大重傳次數(shù)是多少?

TCP 第二次握手 SYN、ACK 包的最大重傳次數(shù)是通過(guò) tcp_synack_retries 內(nèi)核參數(shù)限制的,其默認(rèn)值如下:

$ cat /proc/sys/net/ipv4/tcp_synack_retries
5

是的,TCP 第二次握手 SYN、ACK 包的最大重傳次數(shù)默認(rèn)值是 5 次。

為了驗(yàn)證 SYN、ACK 包最大重傳次數(shù)是 5 次,我們繼續(xù)做下實(shí)驗(yàn),我們先把客戶端的 tcp_syn_retries 設(shè)置為 1,表示客戶端 SYN 最大超時(shí)次數(shù)是 1 次,目的是為了防止多次重傳 SYN,把服務(wù)端 SYN、ACK 超時(shí)定時(shí)器重置。

接著,還是如上面的步驟:

  1. 客戶端配置防火墻屏蔽服務(wù)端的數(shù)據(jù)包

  2. 客戶端 tcpdump 抓取 curl 執(zhí)行時(shí)的數(shù)據(jù)包

把抓取的數(shù)據(jù)包,用 Wireshark 打開(kāi)分析,顯示的時(shí)序圖如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

從上圖,我們可以分析出:

  • 客戶端的 SYN 只超時(shí)重傳了 1 次,因?yàn)?tcp_syn_retries 值為 1

  • 服務(wù)端應(yīng)答了客戶端超時(shí)重傳的 SYN 包后,由于一直收不到客戶端的 ACK 包,所以服務(wù)端一直在超時(shí)重傳 SYN、ACK 包,每次的 RTO 也是指數(shù)上漲的,一共超時(shí)重傳了 5 次,因?yàn)?tcp_synack_retries 值為 5

接著,我把 tcp_synack_retries 設(shè)置為 2,tcp_syn_retries 依然設(shè)置為 1:

echo 2 > /proc/sys/net/ipv4/tcp_synack_retries
echo 1 > /proc/sys/net/ipv4/tcp_syn_retries

依然保持一樣的實(shí)驗(yàn)步驟進(jìn)行操作,接著把抓取的數(shù)據(jù)包,用 Wireshark 打開(kāi)分析,顯示的時(shí)序圖如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

可見(jiàn):

  • 客戶端的 SYN 包只超時(shí)重傳了 1 次,符合 tcp_syn_retries 設(shè)置的值;

  • 服務(wù)端的 SYN、ACK 超時(shí)重傳了 2 次,符合 tcp_synack_retries 設(shè)置的值


實(shí)驗(yàn)二的實(shí)驗(yàn)小結(jié)

通過(guò)實(shí)驗(yàn)二的實(shí)驗(yàn)結(jié)果,我們可以得知,當(dāng) TCP 第二次握手 SYN、ACK 包丟了后,客戶端 SYN 包會(huì)發(fā)生超時(shí)重傳,服務(wù)端 SYN、ACK 也會(huì)發(fā)生超時(shí)重傳。

客戶端 SYN 包超時(shí)重傳的最大次數(shù),是由 tcp_syn_retries 決定的,默認(rèn)值是 5 次;服務(wù)端 SYN、ACK 包時(shí)重傳的最大次數(shù),是由 tcp_synack_retries 決定的,默認(rèn)值是 5 次。

實(shí)驗(yàn)三:TCP 第三次握手 ACK 丟包

為了模擬 TCP 第三次握手 ACK 包丟,我的實(shí)驗(yàn)方法是在服務(wù)端配置防火墻,屏蔽客戶端 TCP 報(bào)文中標(biāo)志位是 ACK 的包,也就是當(dāng)服務(wù)端收到客戶端的 TCP ACK 的報(bào)文時(shí)就會(huì)丟棄,iptables 配置命令如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

接著,在客戶端執(zhí)行如下 tcpdump 命令:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

然后,客戶端向服務(wù)端發(fā)起 telnet,因?yàn)?telnet 命令是會(huì)發(fā)起 TCP 連接,所以用此命令做測(cè)試:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

此時(shí),由于服務(wù)端收不到第三次握手的 ACK 包,所以一直處于 SYN_RECV 狀態(tài):

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

而客戶端是已完成 TCP 連接建立,處于 ESTABLISHED 狀態(tài):

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

過(guò)了 1 分鐘后,觀察發(fā)現(xiàn)服務(wù)端的 TCP 連接不見(jiàn)了:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

過(guò)了 30 分別,客戶端依然還是處于 ESTABLISHED 狀態(tài):

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

接著,在剛才客戶端建立的 telnet 會(huì)話,輸入 123456 字符,進(jìn)行發(fā)送:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

持續(xù)「好長(zhǎng)」一段時(shí)間,客戶端的 telnet 才斷開(kāi)連接:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

以上就是本次的實(shí)現(xiàn)三的現(xiàn)象,這里存在兩個(gè)疑點(diǎn):

  • 為什么服務(wù)端原本處于 SYN_RECV 狀態(tài)的連接,過(guò) 1 分鐘后就消失了?

  • 為什么客戶端 telnet 輸入 123456 字符后,過(guò)了好長(zhǎng)一段時(shí)間,telnet 才斷開(kāi)連接?

不著急,我們把剛抓的數(shù)據(jù)包,用 Wireshark 打開(kāi)分析,顯示的時(shí)序圖如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

上圖的流程:

  • 客戶端發(fā)送 SYN 包給服務(wù)端,服務(wù)端收到后,回了個(gè) SYN、ACK 包給客戶端,此時(shí)服務(wù)端的 TCP 連接處于 SYN_RECV 狀態(tài);

  • 客戶端收到服務(wù)端的  SYN、ACK 包后,給服務(wù)端回了個(gè) ACK 包,此時(shí)客戶端的 TCP 連接處于 ESTABLISHED 狀態(tài);

  • 由于服務(wù)端配置了防火墻,屏蔽了客戶端的 ACK 包,所以服務(wù)端一直處于 SYN_RECV 狀態(tài),沒(méi)有進(jìn)入  ESTABLISHED 狀態(tài),tcpdump 之所以能抓到客戶端的 ACK 包,是因?yàn)閿?shù)據(jù)包進(jìn)入系統(tǒng)的順序是先進(jìn)入 tcpudmp,后經(jīng)過(guò) iptables;

  • 接著,服務(wù)端超時(shí)重傳了 SYN、ACK 包,重傳了 5 次后,也就是超過(guò) tcp_synack_retries 的值(默認(rèn)值是 5),然后就沒(méi)有繼續(xù)重傳了,此時(shí)服務(wù)端的 TCP 連接主動(dòng)中止了,所以剛才處于 SYN_RECV 狀態(tài)的 TCP 連接斷開(kāi)了,而客戶端依然處于ESTABLISHED 狀態(tài);

  • 雖然服務(wù)端 TCP 斷開(kāi)了,但過(guò)了一段時(shí)間,發(fā)現(xiàn)客戶端依然處于ESTABLISHED 狀態(tài),于是就在客戶端的 telnet 會(huì)話輸入了 123456 字符;

  • 此時(shí)由于服務(wù)端已經(jīng)斷開(kāi)連接,客戶端發(fā)送的數(shù)據(jù)報(bào)文,一直在超時(shí)重傳,每一次重傳,RTO 的值是指數(shù)增長(zhǎng)的,所以持續(xù)了好長(zhǎng)一段時(shí)間,客戶端的 telnet 才報(bào)錯(cuò)退出了,此時(shí)共重傳了 15 次。

通過(guò)這一波分析,剛才的兩個(gè)疑點(diǎn)已經(jīng)解除了:

  • 服務(wù)端在重傳 SYN、ACK 包時(shí),超過(guò)了最大重傳次數(shù) tcp_synack_retries,于是服務(wù)端的 TCP 連接主動(dòng)斷開(kāi)了。

  • 客戶端向服務(wù)端發(fā)送數(shù)據(jù)包時(shí),由于服務(wù)端的 TCP 連接已經(jīng)退出了,所以數(shù)據(jù)包一直在超時(shí)重傳,共重傳了 15 次, telnet 就 斷開(kāi)了連接。


TCP 第一次握手的 SYN 包超時(shí)重傳最大次數(shù)是由 tcp_syn_retries 指定,TCP 第二次握手的 SYN、ACK 包超時(shí)重傳最大次數(shù)是由 tcp_synack_retries 指定,那 TCP 建立連接后的數(shù)據(jù)包最大超時(shí)重傳次數(shù)是由什么參數(shù)指定呢?

TCP 建立連接后的數(shù)據(jù)包傳輸,最大超時(shí)重傳次數(shù)是由 tcp_retries2 指定,默認(rèn)值是 15 次,如下:

$ cat /proc/sys/net/ipv4/tcp_retries2
15

如果 15 次重傳都做完了,TCP 就會(huì)告訴應(yīng)用層說(shuō):“搞不定了,包怎么都傳不過(guò)去!”

那如果客戶端不發(fā)送數(shù)據(jù),什么時(shí)候才會(huì)斷開(kāi)處于 ESTABLISHED 狀態(tài)的連接?

這里就需要提到 TCP 的 ?;顧C(jī)制。這個(gè)機(jī)制的原理是這樣的:

定義一個(gè)時(shí)間段,在這個(gè)時(shí)間段內(nèi),如果沒(méi)有任何連接相關(guān)的活動(dòng),TCP ?;顧C(jī)制會(huì)開(kāi)始作用,每隔一個(gè)時(shí)間間隔,發(fā)送一個(gè)「探測(cè)報(bào)文」,該探測(cè)報(bào)文包含的數(shù)據(jù)非常少,如果連續(xù)幾個(gè)探測(cè)報(bào)文都沒(méi)有得到響應(yīng),則認(rèn)為當(dāng)前的 TCP 連接已經(jīng)死亡,系統(tǒng)內(nèi)核將錯(cuò)誤信息通知給上層應(yīng)用程序。

在 Linux 內(nèi)核可以有對(duì)應(yīng)的參數(shù)可以設(shè)置?;顣r(shí)間、?;钐綔y(cè)的次數(shù)、?;钐綔y(cè)的時(shí)間間隔,以下都為默認(rèn)值:

net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75  
net.ipv4.tcp_keepalive_probes=9


  • tcp_keepalive_time=7200:表示?;顣r(shí)間是 7200 秒(2小時(shí)),也就 2 小時(shí)內(nèi)如果沒(méi)有任何連接相關(guān)的活動(dòng),則會(huì)啟動(dòng)保活機(jī)制

  • tcp_keepalive_intvl=75:表示每次檢測(cè)間隔 75 秒;

  • tcp_keepalive_probes=9:表示檢測(cè) 9 次無(wú)響應(yīng),認(rèn)為對(duì)方是不可達(dá)的,從而中斷本次的連接。

也就是說(shuō)在 Linux 系統(tǒng)中,最少需要經(jīng)過(guò) 2 小時(shí) 11 分 15 秒才可以發(fā)現(xiàn)一個(gè)「死亡」連接。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

這個(gè)時(shí)間是有點(diǎn)長(zhǎng)的,所以如果我抓包足夠久,或許能抓到探測(cè)報(bào)文。

實(shí)驗(yàn)三的實(shí)驗(yàn)小結(jié)

在建立 TCP 連接時(shí),如果第三次握手的 ACK,服務(wù)端無(wú)法收到,則服務(wù)端就會(huì)短暫處于 SYN_RECV 狀態(tài),而客戶端會(huì)處于 ESTABLISHED 狀態(tài)。

由于服務(wù)端一直收不到 TCP 第三次握手的 ACK,則會(huì)一直重傳 SYN、ACK 包,直到重傳次數(shù)超過(guò) tcp_synack_retries 值(默認(rèn)值 5 次)后,服務(wù)端就會(huì)斷開(kāi) TCP 連接。

而客戶端則會(huì)有兩種情況:

  • 如果客戶端沒(méi)發(fā)送數(shù)據(jù)包,一直處于 ESTABLISHED 狀態(tài),然后經(jīng)過(guò) 2 小時(shí) 11 分 15 秒才可以發(fā)現(xiàn)一個(gè)「死亡」連接,于是客戶端連接就會(huì)斷開(kāi)連接。

  • 如果客戶端發(fā)送了數(shù)據(jù)包,一直沒(méi)有收到服務(wù)端對(duì)該數(shù)據(jù)包的確認(rèn)報(bào)文,則會(huì)一直重傳該數(shù)據(jù)包,直到重傳次數(shù)超過(guò) tcp_retries2 值(默認(rèn)值 15 次)后,客戶端就會(huì)斷開(kāi) TCP 連接。


TCP 快速建立連接

客戶端在向服務(wù)端發(fā)起 HTTP GET 請(qǐng)求時(shí),一個(gè)完整的交互過(guò)程,需要 2.5 個(gè) RTT 的時(shí)延。

由于第三次握手是可以攜帶數(shù)據(jù)的,這時(shí)如果在第三次握手發(fā)起 HTTP GET 請(qǐng)求,需要 2 個(gè) RTT 的時(shí)延。

但是在下一次(不是同個(gè) TCP 連接的下一次)發(fā)起 HTTP GET 請(qǐng)求時(shí),經(jīng)歷的 RTT 也是一樣,如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
常規(guī) HTTP 請(qǐng)求

在 Linux 3.7 內(nèi)核版本中,提供了 TCP Fast Open 功能,這個(gè)功能可以減少 TCP 連接建立的時(shí)延。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
常規(guī) HTTP 請(qǐng)求 與 Fast  Open HTTP 請(qǐng)求

  • 在第一次建立連接的時(shí)候,服務(wù)端在第二次握手產(chǎn)生一個(gè) Cookie (已加密)并通過(guò) SYN、ACK 包一起發(fā)給客戶端,于是客戶端就會(huì)緩存這個(gè) Cookie,所以第一次發(fā)起 HTTP Get 請(qǐng)求的時(shí)候,還是需要 2 個(gè) RTT 的時(shí)延;

  • 在下次請(qǐng)求的時(shí)候,客戶端在 SYN 包帶上 Cookie 發(fā)給服務(wù)端,就提前可以跳過(guò)三次握手的過(guò)程,因?yàn)?Cookie 中維護(hù)了一些信息,服務(wù)端可以從 Cookie 獲取 TCP 相關(guān)的信息,這時(shí)發(fā)起的 HTTP GET 請(qǐng)求就只需要 1 個(gè) RTT 的時(shí)延;

注:客戶端在請(qǐng)求并存儲(chǔ)了 Fast Open Cookie 之后,可以不斷重復(fù) TCP Fast Open 直至服務(wù)器認(rèn)為 Cookie 無(wú)效(通常為過(guò)期)

在 Linux 上如何打開(kāi) Fast Open 功能?

可以通過(guò)設(shè)置 net.ipv4.tcp_fastopn 內(nèi)核參數(shù),來(lái)打開(kāi) Fast Open 功能。

net.ipv4.tcp_fastopn 各個(gè)值的意義:

  • 0 關(guān)閉

  • 1 作為客戶端使用 Fast Open 功能

  • 2 作為服務(wù)端使用 Fast Open 功能

  • 3 無(wú)論作為客戶端還是服務(wù)器,都可以使用 Fast Open 功能


TCP Fast Open 抓包分析

在下圖,數(shù)據(jù)包 7 號(hào),客戶端發(fā)起了第二次 TCP 連接時(shí),SYN 包會(huì)攜帶 Cooike,并且有長(zhǎng)度為 5 的數(shù)據(jù)。

服務(wù)端收到后,校驗(yàn) Cooike 合法,于是就回了 SYN、ACK 包,并且確認(rèn)應(yīng)答收到了客戶端的數(shù)據(jù)包,ACK = 5 + 1 = 6

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP Fast Open 抓包分析

TCP 重復(fù)確認(rèn)和快速重傳

當(dāng)接收方收到亂序數(shù)據(jù)包時(shí),會(huì)發(fā)送重復(fù)的 ACK,以使告知發(fā)送方要重發(fā)該數(shù)據(jù)包,當(dāng)發(fā)送方收到 3 個(gè)重復(fù) ACK 時(shí),就會(huì)觸發(fā)快速重傳,立該重發(fā)丟失數(shù)據(jù)包。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
快速重傳機(jī)制

TCP 重復(fù)確認(rèn)和快速重傳的一個(gè)案例,用 Wireshark 分析,顯示如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

  • 數(shù)據(jù)包 1 期望的下一個(gè)數(shù)據(jù)包 Seq 是 1,但是數(shù)據(jù)包 2 發(fā)送的 Seq 卻是 10945,說(shuō)明收到的是亂序數(shù)據(jù)包,于是回了數(shù)據(jù)包 3 ,還是同樣的 Seq = 1,Ack = 1,這表明是重復(fù)的 ACK;

  • 數(shù)據(jù)包 4 和 6 依然是亂序的數(shù)據(jù)包,于是依然回了重復(fù)的 ACK;

  • 當(dāng)對(duì)方收到三次重復(fù)的 ACK 后,于是就快速重傳了 Seq = 1 、Len = 1368 的數(shù)據(jù)包 8;

  • 當(dāng)收到重傳的數(shù)據(jù)包后,發(fā)現(xiàn) Seq = 1 是期望的數(shù)據(jù)包,于是就發(fā)送了確認(rèn)報(bào)文 ACK;

注意:快速重傳和重復(fù) ACK 標(biāo)記信息是 Wireshark 的功能,非數(shù)據(jù)包本身的信息。

以上案例在 TCP 三次握手時(shí)協(xié)商開(kāi)啟了選擇性確認(rèn) SACK,因此一旦數(shù)據(jù)包丟失并收到重復(fù) ACK ,即使在丟失數(shù)據(jù)包之后還成功接收了其他數(shù)據(jù)包,也只需要重傳丟失的數(shù)據(jù)包。如果不啟用 SACK,就必須重傳丟失包之后的每個(gè)數(shù)據(jù)包。

如果要支持 SACK,必須雙方都要支持。在 Linux 下,可以通過(guò) net.ipv4.tcp_sack 參數(shù)打開(kāi)這個(gè)功能(Linux 2.4 后默認(rèn)打開(kāi))。


TCP 流量控制

TCP 為了防止發(fā)送方無(wú)腦的發(fā)送數(shù)據(jù),導(dǎo)致接收方緩沖區(qū)被填滿,所以就有了滑動(dòng)窗口的機(jī)制,它可利用接收方的接收窗口來(lái)控制發(fā)送方要發(fā)送的數(shù)據(jù)量,也就是流量控制。

接收窗口是由接收方指定的值,存儲(chǔ)在 TCP 頭部中,它可以告訴發(fā)送方自己的 TCP 緩沖空間區(qū)大小,這個(gè)緩沖區(qū)是給應(yīng)用程序讀取數(shù)據(jù)的空間:

  • 如果應(yīng)用程序讀取了緩沖區(qū)的數(shù)據(jù),那么緩沖空間區(qū)的就會(huì)把被讀取的數(shù)據(jù)移除

  • 如果應(yīng)用程序沒(méi)有讀取數(shù)據(jù),則數(shù)據(jù)會(huì)一直滯留在緩沖區(qū)。

接收窗口的大小,是在 TCP 三次握手中協(xié)商好的,后續(xù)數(shù)據(jù)傳輸時(shí),接收方發(fā)送確認(rèn)應(yīng)答 ACK 報(bào)文時(shí),會(huì)攜帶當(dāng)前的接收窗口的大小,以此來(lái)告知發(fā)送方。

假設(shè)接收方接收到數(shù)據(jù)后,應(yīng)用層能很快的從緩沖區(qū)里讀取數(shù)據(jù),那么窗口大小會(huì)一直保持不變,過(guò)程如下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
理想狀態(tài)下的窗口變化

但是現(xiàn)實(shí)中服務(wù)器會(huì)出現(xiàn)繁忙的情況,當(dāng)應(yīng)用程序讀取速度慢,那么緩存空間會(huì)慢慢被占滿,于是為了保證發(fā)送方發(fā)送的數(shù)據(jù)不會(huì)超過(guò)緩沖區(qū)大小,則服務(wù)器會(huì)調(diào)整窗口大小的值,接著通過(guò) ACK 報(bào)文通知給對(duì)方,告知現(xiàn)在的接收窗口大小,從而控制發(fā)送方發(fā)送的數(shù)據(jù)大小。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
服務(wù)端繁忙狀態(tài)下的窗口變化

零窗口通知與窗口探測(cè)

假設(shè)接收方處理數(shù)據(jù)的速度跟不上接收數(shù)據(jù)的速度,緩存就會(huì)被占滿,從而導(dǎo)致接收窗口為 0,當(dāng)發(fā)送方接收到零窗口通知時(shí),就會(huì)停止發(fā)送數(shù)據(jù)。

如下圖,可以接收方的窗口大小在不斷的收縮至 0:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
窗口大小在收縮

接著,發(fā)送方會(huì)定時(shí)發(fā)送窗口大小探測(cè)報(bào)文,以便及時(shí)知道接收方窗口大小的變化。

以下圖 Wireshark 分析圖作為例子說(shuō)明:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
零窗口 與 窗口探測(cè)

  • 發(fā)送方發(fā)送了數(shù)據(jù)包 1 給接收方,接收方收到后,由于緩沖區(qū)被占滿,回了個(gè)零窗口通知;

  • 發(fā)送方收到零窗口通知后,就不再發(fā)送數(shù)據(jù)了,直到過(guò)了 3.4 秒后,發(fā)送了一個(gè) TCP Keep-Alive 報(bào)文,也就是窗口大小探測(cè)報(bào)文;

  • 當(dāng)接收方收到窗口探測(cè)報(bào)文后,就立馬回一個(gè)窗口通知,但是窗口大小還是 0;

  • 發(fā)送方發(fā)現(xiàn)窗口還是 0,于是繼續(xù)等待了 6.8(翻倍) 秒后,又發(fā)送了窗口探測(cè)報(bào)文,接收方依然還是回了窗口為 0 的通知;

  • 發(fā)送方發(fā)現(xiàn)窗口還是 0,于是繼續(xù)等待了 13.5(翻倍) 秒后,又發(fā)送了窗口探測(cè)報(bào)文,接收方依然還是回了窗口為 0 的通知;

可以發(fā)現(xiàn),這些窗口探測(cè)報(bào)文以 3.4s、6.5s、13.5s 的間隔出現(xiàn),說(shuō)明超時(shí)時(shí)間會(huì)翻倍遞增。

這連接暫停了 25s,想象一下你在打王者的時(shí)候,25s 的延遲你還能上王者嗎?

發(fā)送窗口的分析

在 Wireshark 看到的 Windows size 也就是 " win = ",這個(gè)值表示發(fā)送窗口嗎?

這不是發(fā)送窗口,而是在向?qū)Ψ铰暶髯约旱慕邮沾翱凇?/p>

你可能會(huì)好奇,抓包文件里有「Window size scaling factor」,它其實(shí)是算出實(shí)際窗口大小的乘法因子,「Windos size value」實(shí)際上并不是真實(shí)的窗口大小,真實(shí)窗口大小的計(jì)算公式如下:

「Windos size value」 * 「Window size scaling factor」 = 「Caculated window size 」

對(duì)應(yīng)的下圖案例,也就是 32 * 2048 = 65536。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

實(shí)際上是 Caculated window size 的值是 Wireshark 工具幫我們算好的,Window size scaling factor 和 Windos size value 的值是在 TCP 頭部中,其中 Window size scaling factor 是在三次握手過(guò)程中確定的,如果你抓包的數(shù)據(jù)沒(méi)有 TCP 三次握手,那可能就無(wú)法算出真實(shí)的窗口大小的值,如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

如何在包里看出發(fā)送窗口的大???

很遺憾,沒(méi)有簡(jiǎn)單的辦法,發(fā)送窗口雖然是由接收窗口決定,但是它又可以被網(wǎng)絡(luò)因素影響,也就是擁塞窗口,實(shí)際上發(fā)送窗口是值是 min(擁塞窗口,接收窗口)。

發(fā)送窗口和 MSS 有什么關(guān)系?

發(fā)送窗口決定了一口氣能發(fā)多少字節(jié),而 MSS 決定了這些字節(jié)要分多少包才能發(fā)完。

舉個(gè)例子,如果發(fā)送窗口為 16000 字節(jié)的情況下,如果 MSS 是 1000 字節(jié),那就需要發(fā)送 1600/1000 = 16 個(gè)包。

發(fā)送方在一個(gè)窗口發(fā)出 n 個(gè)包,是不是需要 n 個(gè) ACK 確認(rèn)報(bào)文?

不一定,因?yàn)?TCP 有累計(jì)確認(rèn)機(jī)制,所以當(dāng)收到多個(gè)數(shù)據(jù)包時(shí),只需要應(yīng)答最后一個(gè)數(shù)據(jù)包的 ACK 報(bào)文就可以了。


TCP 延遲確認(rèn)與 Nagle 算法

當(dāng)我們 TCP 報(bào)文的承載的數(shù)據(jù)非常小的時(shí)候,例如幾個(gè)字節(jié),那么整個(gè)網(wǎng)絡(luò)的效率是很低的,因?yàn)槊總€(gè) TCP 報(bào)文中都有會(huì) 20 個(gè)字節(jié)的 TCP 頭部,也會(huì)有 20 個(gè)字節(jié)的 IP 頭部,而數(shù)據(jù)只有幾個(gè)字節(jié),所以在整個(gè)報(bào)文中有效數(shù)據(jù)占有的比重就會(huì)非常低。

這就好像快遞員開(kāi)著大貨車送一個(gè)小包裹一樣浪費(fèi)。

那么就出現(xiàn)了常見(jiàn)的兩種策略,來(lái)減少小報(bào)文的傳輸,分別是:

  • Nagle 算法

  • 延遲確認(rèn)


Nagle 算法是如何避免大量 TCP 小數(shù)據(jù)報(bào)文的傳輸?

Nagle 算法做了一些策略來(lái)避免過(guò)多的小數(shù)據(jù)報(bào)文發(fā)送,這可提高傳輸效率。

Nagle 算法的策略:

  • 沒(méi)有已發(fā)送未確認(rèn)報(bào)文時(shí),立刻發(fā)送數(shù)據(jù)。

  • 存在未確認(rèn)報(bào)文時(shí),直到「沒(méi)有已發(fā)送未確認(rèn)報(bào)文」或「數(shù)據(jù)長(zhǎng)度達(dá)到 MSS 大小」時(shí),再發(fā)送數(shù)據(jù)。

只要沒(méi)滿足上面條件中的一條,發(fā)送方一直在囤積數(shù)據(jù),直到滿足上面的發(fā)送條件。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
禁用 Nagle 算法 與 啟用 Nagle 算法

上圖右側(cè)啟用了 Nagle 算法,它的發(fā)送數(shù)據(jù)的過(guò)程:

  • 一開(kāi)始由于沒(méi)有已發(fā)送未確認(rèn)的報(bào)文,所以就立刻發(fā)了 H 字符;

  • 接著,在還沒(méi)收到對(duì) H 字符的確認(rèn)報(bào)文時(shí),發(fā)送方就一直在囤積數(shù)據(jù),直到收到了確認(rèn)報(bào)文后,此時(shí)就沒(méi)有已發(fā)送未確認(rèn)的報(bào)文,于是就把囤積后的 ELL 字符一起發(fā)給了接收方;

  • 待收到對(duì) ELL 字符的確認(rèn)報(bào)文后,于是把最后一個(gè) O 字符發(fā)送出去

可以看出,Nagle 算法一定會(huì)有一個(gè)小報(bào)文,也就是在最開(kāi)始的時(shí)候。

另外,Nagle 算法默認(rèn)是打開(kāi)的,如果對(duì)于一些需要小數(shù)據(jù)包交互的場(chǎng)景的程序,比如,telnet 或 ssh 這樣的交互性比較強(qiáng)的程序,則需要關(guān)閉 Nagle 算法。

可以在 Socket 設(shè)置 TCP_NODELAY 選項(xiàng)來(lái)關(guān)閉這個(gè)算法(關(guān)閉 Nagle 算法沒(méi)有全局參數(shù),需要根據(jù)每個(gè)應(yīng)用自己的特點(diǎn)來(lái)關(guān)閉)。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
關(guān)閉 Nagle 算法

那延遲確認(rèn)又是什么?

事實(shí)上當(dāng)沒(méi)有攜帶數(shù)據(jù)的 ACK,他的網(wǎng)絡(luò)效率也是很低的,因?yàn)樗灿?40 個(gè)字節(jié)的 IP 頭 和 TCP 頭,但沒(méi)有攜帶數(shù)據(jù)。

為了解決 ACK 傳輸效率低問(wèn)題,所以就衍生出了 TCP 延遲確認(rèn)。

TCP 延遲確認(rèn)的策略:

  • 當(dāng)有響應(yīng)數(shù)據(jù)要發(fā)送時(shí),ACK 會(huì)隨著響應(yīng)數(shù)據(jù)一起立刻發(fā)送給對(duì)方

  • 當(dāng)沒(méi)有響應(yīng)數(shù)據(jù)要發(fā)送時(shí),ACK 將會(huì)延遲一段時(shí)間,以等待是否有響應(yīng)數(shù)據(jù)可以一起發(fā)送

  • 如果在延遲等待發(fā)送 ACK 期間,對(duì)方的第二個(gè)數(shù)據(jù)報(bào)文又到達(dá)了,這時(shí)就會(huì)立刻發(fā)送 ACK


實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP 延遲確認(rèn)

延遲等待的時(shí)間是在 Linux 內(nèi)核中的定義的,如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

關(guān)鍵就需要 HZ 這個(gè)數(shù)值大小,HZ 是跟系統(tǒng)的時(shí)鐘頻率有關(guān),每個(gè)操作系統(tǒng)都不一樣,在我的 Linux 系統(tǒng)中 HZ 大小是 1000,如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

知道了 HZ 的大小,那么就可以算出:

  • 最大延遲確認(rèn)時(shí)間是 200 ms (1000/5)

  • 最短延遲確認(rèn)時(shí)間是 40 ms (1000/25)

TCP 延遲確認(rèn)可以在 Socket 設(shè)置 TCP_QUICKACK 選項(xiàng)來(lái)關(guān)閉這個(gè)算法。

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
關(guān)閉 TCP 延遲確認(rèn)

延遲確認(rèn) 和 Nagle 算法混合使用時(shí),會(huì)產(chǎn)生新的問(wèn)題

當(dāng) TCP 延遲確認(rèn) 和 Nagle 算法混合使用時(shí),會(huì)導(dǎo)致時(shí)耗增長(zhǎng),如下圖:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP
TCP 延遲確認(rèn) 和 Nagle 算法混合使用

發(fā)送方使用了 Nagle 算法,接收方使用了 TCP 延遲確認(rèn)會(huì)發(fā)生如下的過(guò)程:

  • 發(fā)送方先發(fā)出一個(gè)小報(bào)文,接收方收到后,由于延遲確認(rèn)機(jī)制,自己又沒(méi)有要發(fā)送的數(shù)據(jù),只能干等著發(fā)送方的下一個(gè)報(bào)文到達(dá);

  • 而發(fā)送方由于 Nagle 算法機(jī)制,在未收到第一個(gè)報(bào)文的確認(rèn)前,是不會(huì)發(fā)送后續(xù)的數(shù)據(jù);

  • 所以接收方只能等待最大時(shí)間 200 ms 后,才回 ACK 報(bào)文,發(fā)送方收到第一個(gè)報(bào)文的確認(rèn)報(bào)文后,也才可以發(fā)送后續(xù)的數(shù)據(jù)。

很明顯,這兩個(gè)同時(shí)使用會(huì)造成額外的時(shí)延,這就會(huì)使得網(wǎng)絡(luò)"很慢"的感覺(jué)。

要解決這個(gè)問(wèn)題,只有兩個(gè)辦法:

  • 要么發(fā)送方關(guān)閉 Nagle 算法

  • 要么接收方關(guān)閉 TCP 延遲確認(rèn)


巨人的肩膀

[1] Wireshark網(wǎng)絡(luò)分析的藝術(shù).林沛滿.人民郵電出版社.

[2] Wireshark網(wǎng)絡(luò)分析就這么簡(jiǎn)單.林沛滿.人民郵電出版社.

[3] Wireshark數(shù)據(jù)包分析實(shí)戰(zhàn).Chris Sanders .人民郵電出版社.

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

長(zhǎng)按訂閱更多精彩▼

實(shí)戰(zhàn)!我用“大白鯊”讓你看見(jiàn) TCP

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開(kāi)發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開(kāi)幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉