能ping通,TCP就一定能連通嗎?

平時,我們想要知道,自己的機器到目的機器之間,

網路通不通

,一般會執行

ping命令

一般對於狀況良好的網路來說,你能看到它對應的

loss

丟包率為

0%

,也就是所謂的

能ping通

。如果看到丟包率

100%

,也就是

ping不通

能ping通,TCP就一定能連通嗎?

ping正常

能ping通,TCP就一定能連通嗎?

ping不通

那麼問題來了,假設我能

ping

通某臺機器,那這時候如果我改用

TCP協議

去發資料到目的機器,

也一定能通嗎?

或者換個問法,

ping和tcp協議走的網路路徑是一樣的嗎?

這時候第一反應就是

不一定

,因為ping完之後中間鏈路裡的

某個路由器可能會掛了(斷電了)

,再用TCP去連就會走別的路徑。

也沒錯。但假設,

中間鏈路沒發生任何變化呢?

我先直接說答案。

不一定,走的網路路徑還是有可能是不同的。

今天就來聊聊為什麼。

我之前寫過一篇《斷網了,還能ping通 127。0。0。1 嗎?》,裡面提到過

ping資料包和tcp資料包的區別

能ping通,TCP就一定能連通嗎?

ping和TCP發訊息的區別

我們知道網路是分層的,每一層都有對應協議。

能ping通,TCP就一定能連通嗎?

五層網路協議對應的訊息體變化分析

而這網路層就像搭積木一樣,上層協議都是基於下層協議搭出來的。

不管是ping(用了ICMP協議)還是tcp本質上都是基於網路層IP協議的資料包,而到了物理層,都是二進位制01串,都走網絡卡發出去了。

如果網路環境沒發生變化,目的地又一樣,那按道理說他們走的網路路徑應該是一樣的,什麼情況下會不同呢?

我們就從

路由

這個話題聊起吧。

網路路徑

在我們的想象中,當我們想在兩臺機器之間傳輸資料。本機和目的機器之間會建立一條連線,像

一條管道

一樣,資料從這頭到那頭。這條管道其實是我們為了方便理解而抽象出來的概念。

實際上,我們將資料包從本地網絡卡發出之後,會經過各種

路由器(或者交換機)

,才能到達目的機器。

這些路由器數量眾多,相互之間可以互連,連起來之後就像是一張大網,所以叫

"網路"

可以說是非常的形象。

能ping通,TCP就一定能連通嗎?

路由器構成的網路

考慮到交換機有的功能,路由器基本上都支援,所以我們這邊只討論路由器。

那麼現在問題來了,

路由器收到資料後,怎麼知道應該走哪條路徑,傳給哪個路由器?

路徑由什麼決定?

在上面的那麼大一張網路中,隨便一個路由器都有可能走任何一個路徑,將資料發到另外一個路由器上,

但路由和路由之間距離,頻寬啥的可能都不同。

於是就很需要知道,

兩點之間走哪條路才是最優路徑

於是問題就變成了這樣一個

圖狀結構

。每條邊都帶有

成本或權重

,算這上面

任意兩點的最短距離

能ping通,TCP就一定能連通嗎?

路由器和Dijkstra

這時候想必大家回憶壓不住要上來了。

這題我熟,這就是大學時候刷的

Dijkstra演算法

。菊花廠的OJ筆試題集裡也經常出現,現在終於明白為什麼他們家的筆試題裡圖類題目比別的大廠貌似要多一些了吧,因為菊花廠就是搞通訊的,做路由器的老玩家了。

路由表的生成

基於

Dijkstra演算法

,封裝出了一個新的協議,

OSPF協議

O

pen

S

hortest

P

ath

F

irst, 開放最短路徑優先)。

有了OSPF,路由器就得到了網路圖裡自己到其他點之間的

最短距離

,於是就知道了

資料包要到某個點,該走哪條最優路徑

將這些資訊匯成一張表,也就是我們常說的

路由表

路由表裡記錄了到什麼IP需要走什麼埠,以及走這條路徑的成本(

metric

)。

可以透過

route

命令檢視到。

能ping通,TCP就一定能連通嗎?

route表

路由表決定資料包路徑

資料包在傳送的過程中,會在

網路層

加入

目標地址IP

路由器會根據這個

IP

路由表

去做匹配。

然後路由表,會告訴路由器,什麼樣的訊息該轉發到什麼埠。

舉個例子。

能ping通,TCP就一定能連通嗎?

透過路由錶轉發資料

假設A要發訊息到D。也就是

192。168。0。105/24

要發訊息到

192。168。1。11/24

那麼A會把訊息經發到路由器。

路由器已知目的地IP

192。168。1。11/24

,去跟

路由表

做匹配,發現

192。168。1。0/24

, 就在e2埠,那麼就會把訊息從e2埠發出,(可能還會經過交換機)最後把訊息打到目的機器。

當然,如果路由表裡找不到,那就打到

預設閘道器

吧,也就是從e1口發出,發到IP

192。0。2。1

這個路由器的路由表不知道該去哪,說不定其他路由器知道

路由表的匹配規則

上面的例子裡,是隻匹配上了路由表裡的

一項

,所以只能是它了。

但是,條條大路通羅馬。實際上能到目的地的路徑肯定有很多。

如果路由表裡有很多項都被匹配上了,會怎麼選?

如果多個路由項都能到目的地,那就優先選

匹配長度更長

的那個。比如,還是目的地

192。168。1。11

,發現路由表裡的

192.168.1

。0/

24

192.168

。0。0/

16

都能匹配上,但明顯

前者匹配長度更長

,所以最後會走

192.168.1

。0/

24

對應的轉發埠。

但如果兩個表項的匹配長度都一樣呢?

那就會看生成這個路由表項的

協議

是啥,選優先順序高的,優先順序越高也就是所謂的

管理距離

AD

A

dministrative

D

istance)越小。比如說優先選

手動配

的靜態(

static

)路由,次優選

OSPF

動態學習過來的表項。

如果還是相同,就看

度量值metrics

,其實也就是

路徑成本cost

,成本越小,越容易被選中。

路由器能選的路線有很多,但按道理,最優的只有"一條",所以到這裡為止,我們都可以認為,對於同一個目的地,ping和TCP走的路徑是相同的。

但是。

如果連路徑成本都一樣呢?

也就是說有多條最優路徑呢。

那就都用

這也就是所謂的

等價多路徑,ECMP

E

qual

C

ost

M

ulti

P

ath)。

我們可以透過

traceroute

看下鏈路是否存在等價多路徑的情況。

能ping通,TCP就一定能連通嗎?

可以看到,中間某幾行,有

好幾個IP

,也就是說這一跳裡同時可以選好幾個目的機器,說明這段路徑

支援ECMP

ECMP有什麼用

利用等價多路徑,我們

可以增加鏈路頻寬

舉個例子。

能ping通,TCP就一定能連通嗎?

沒有ECMP時只能選擇某一條路徑

從A點到B點,如果這兩條路徑成本不同,頻寬都是

1千兆

。那資料包肯定就選成本低的那條路了,如果這條路出故障了,就走下面那條路。但不管怎麼樣,

同一時間,只用到了一條路徑

。另外一條閒置就有些浪費了,有沒有辦法可以利用起來呢?

有,將它們兩條路徑的成本設定成一樣,那它們就成了等價路由,然後中間的路由器開啟

ECMP

特性,就可以同時利用這兩條鏈路了。頻寬就從原來的

1千兆

變成了

2千兆

。資料就可以在兩條路徑中隨意選擇了。

能ping通,TCP就一定能連通嗎?

利用ECMP可以同時使用兩條鏈路

但這也帶來了另外一個問題。

加劇了資料包亂序

原來我只使用一條網路路徑,資料依次發出,如無意外,也是依次到達。

現在兩個資料包走兩條路徑,先發的資料包可能後到。這就亂序了。

那麼問題又又來了。

亂序會有什麼問題?

對於我們最最最常使用的TCP協議來說,它是個可靠性網路的協議,這裡提到的

可靠

,不僅是保證資料要能送到目的地,還要保證

資料順序

要跟原來發送端的一樣。

實現也很簡單,

TCP為每個資料包(segment)做上編號

。資料到了接收端後,根據

資料包編號

發現是

亂序資料包

,就會扔到

亂序佇列

中對資料包進行排序。如果前面的資料包還沒到,哪怕後面的資料包先到了,也得在亂序佇列中一直等,到齊後才能被上層拿到。

舉個例子,傳送端發出三個資料包,

編號1,2,3

,假設在

傳輸層

2和3

先到了,

1

還沒到。那此時

應用層

是沒辦法拿到

2和3

的資料包的,必須得等

1

來了之後,

應用層才能一次性拿到這三個包

。因為這三個包原來可能表示的是一個完整的訊息,少了1, 那麼

訊息就不完整

,應用層拿到了也毫無意義。

像這種,由於

前面的資料丟失

導致

後面的資料沒辦法及時給到應用層

的現象,就是我們常說的

TCP隊頭阻塞

能ping通,TCP就一定能連通嗎?

亂序佇列等待資料包的到來

亂序發生時

2和3

需要待在亂序佇列中,而

亂序佇列其實用的也是接收緩衝區的記憶體

,而

接收緩衝區是有大小限制的

。透過下面的命令可以看到接收緩衝區的大小。

# 檢視接收緩衝區$ sysctl net。ipv4。tcp_rmemnet。ipv4。tcp_rmem = 4096(min) 87380(default) 6291456(max)# 緩衝區會在min和max之間動態調整

亂序的情況越多,接收緩衝區的記憶體就被佔用的越多,對應的

接收視窗

就會變小,那正常能收的資料就變少了,

網路吞吐就變差

了,也就是效能變差了。

因此,我們需要儘量保證所有

同一個TCP連線下的所有TCP包都走相同路徑,這樣才能最大程度避免丟包

ECMP的路徑選擇策略

當初開啟ECMP就是為了提升效能,現在反而加重了亂序,降低了TCP傳輸效能。

這怎麼能忍。

為了解決這個問題,我們需要有一個合理的路徑選擇策略。為了避免同一個連線裡的資料包亂序,我們需要保證同一個連線裡的資料包,都走同樣的路徑。

這好辦。我們可以透過連線的

五元組

(傳送方的

IP

,接收方的

IP

,以及通訊

協議

)資訊定位到唯一一條連線。

能ping通,TCP就一定能連通嗎?

五元組

然後對五元組資訊生成雜湊鍵,讓同一個雜湊鍵的資料走同一條路徑,問題就完美解決了。

能ping通,TCP就一定能連通嗎?

五元組對映成hash鍵

能ping通,TCP就一定能連通嗎?

根據五元組選擇ECMP路徑

TCP和Ping走的網路路徑一樣嗎

現在我們回到文章開頭的問題。

對於同樣的傳送端和接收端,

TCP和Ping走的網路路徑一樣嗎?

不一定一樣,因為

五元組

裡的資訊裡有一項是

通訊協議

。ping用的是

ICMP協議

,跟

TCP協議

不同,並且ping不需要用到埠,所以五元組不同,生成的

雜湊鍵不同

,透過ECMP選擇到的路徑也可能不同。

能ping通,TCP就一定能連通嗎?

TCP和ping的五元組差異

同樣都用TCP協議,資料包走的網路路徑一樣嗎

還是同樣的傳送端和接收端,同樣是TCP協議,不同TCP連線走的網路路徑是一樣的嗎?

跟上面的問題一樣,其實

還是五元組的問題

,同樣都是TCP協議,對於同樣的傳送端和接收端,他們的IP和接收端的埠肯定是一樣的,但

傳送方的埠是可以隨時變化

的,因此透過ECMP走的路徑也可能不同。

能ping通,TCP就一定能連通嗎?

不同TCP連線的五元組差異

但問題又來了。

我知道這個有什麼用呢?我做業務開發,又沒有設定網路路由的許可權。

利用這個知識點排查問題

對於業務開發,這絕對不是個沒用的知識點。

如果某天,你發現,你能ping通目的機器,但用TCP去連,卻

偶爾連不上

目的機器。而且兩端機器都挺空閒,沒什麼效能上的瓶頸。實在

走投無路

了。

你就可以想想,會不會是網路中用到了

ECMP

,其中一條鏈路有問題導致的。

能ping通,TCP就一定能連通嗎?

ping能成功但部分TCP連線失敗

排查方法也很簡單。

你是知道本機的IP以及目的機器的IP和埠號的,也知道自己用的是TCP連線。

只要你在

報錯的時候列印下錯誤資訊,你就知道了傳送端的埠號了。

這樣

五元組

是啥你就知道了。

下一步就是

指定傳送端的埠號重新發起TCP請求,同樣的五元組,走同樣的路徑,按理說如果鏈路有問題,就肯定會復現。

如果不想改自己的程式碼,你可以用

nc命令指定客戶端埠

看下能不能正常建立TCP連線。

nc -p 6666 baidu。com 80

-p 6666

是指定發出請求的客戶端埠是

6666

,後面跟著的是

連線的域名

80埠

能ping通,TCP就一定能連通嗎?

透過nc成功建立tcp連線

假設用了

6666埠

的五元組去連線

總是失敗

,改用

6667或其他埠

卻能成功

,你可以帶著這個資訊去找找負責網路的同事。

總結

路由器可以透過OSPF協議生成路由表,利用資料包裡的IP地址去跟路由表做匹配,選擇最優路徑後進行轉發。

當路由表一個都匹配不上時會走預設閘道器。當匹配上多個的時候,會先看

匹配長度

,如果一樣就看

管理距離

,還一樣就看

路徑成本

。如果連路徑成本都一樣,那

等價路徑

。如果路由開啟了ECMP,那就可以同時利用這幾條路徑做傳輸。

ECMP可以提高鏈路頻寬,同時利用五元組做雜湊鍵進行路徑選擇,保證了同一條連線的資料包走同一條路徑,減少了亂序的情況。

可以透過traceroute命令檢視到鏈路上是否有用到ECMP的情況。

開啟了ECMP的網路鏈路中,TCP和ping命令可能走的路徑不同,甚至同樣是TCP,不同連線之間,走的路徑也不同,因此出現了連線時好時壞的問題,實在是走投無路了,可以考慮下是不是跟ECMP有關。

當然,

遇到問題多懷疑自己,要相信絕大部分時候真的跟ECMP無關

原文連結:https://mp。weixin。qq。com/s/fb2uUWz5ZjPEfYv_l6e4Zg