动图图解!既然IP层会分片,为什么TCP层也还要分段?
文章持續更新,可以微信搜一搜「golang小白成長記」第一時間閱讀,回復【教程】獲golang免費視頻教程。本文已經收錄在GitHub https://github.com/xiaobaiTech/golangFamily ?(點擊閱讀原文直達), 有大廠面試完整考點和成長路線,歡迎Star。
什么是TCP分段和IP分片
我們知道網絡就像一根管子,而管子吧,就會有粗細。
一個數據包想從管子的一端到另一端,得過這個管子。(廢話)
但數據包的量有大有小,想過管子,數據包不能大于這根管子的粗細。
問題來了,數據包過大時怎么辦?
答案比較簡單。會把數據包切分小塊。這樣數據就可以由大變小,順利傳輸。
數據分片回去看下網絡分層協議,數據先過傳輸層,再到網絡層。
這個行為在傳輸層和網絡層都有可能發生。
在傳輸層(TCP協議)里,叫分段。
在網絡層(IP層),叫分片。(注意以下提到的IP沒有特殊說明的情況下,都是指IPV4)
那么不管是分片還是分段,肯定需要按照一定的長度切分。
在TCP里,這個長度是MSS。
在IP層里,這個長度是MTU。
那MSS和MTU是什么關系呢?這個在之前的文章里簡單提到過。這里單獨拿出來。
MSS是什么
MSS:Maximum Segment Size 。TCP 提交給 IP 層最大分段大小,不包含 TCP Header 和 ?TCP Option,只包含 TCP Payload ,MSS 是 TCP 用來限制應用層最大的發送字節數。
假設 MTU= 1500 byte,那么 MSS = 1500- 20(IP Header) -20 (TCP Header) = 1460 byte,如果應用層有 2000 byte 發送,那么需要兩個切片才可以完成發送,第一個 TCP 切片 = 1460,第二個 TCP 切片 = 540。
如何查看MSS?
我們都知道TCP三次握手,而MSS會在三次握手的過程中傳遞給對方,用于通知對端本地最大可以接收的TCP報文數據大小(不包含TCP和IP報文首部)。
抓包mss比如上圖中,B將自己的MSS發送給A,建議A在發數據給B的時候,采用MSS=1420進行分段。而B在發數據給A的時候,同樣會帶上MSS=1372。兩者在對比后,會采用小的那個值(1372)作為通信的MSS值,這個過程叫MSS協商。
另外,一般情況下MSS + 20(TCP頭)+ 20(IP頭)= MTU,上面抓包的圖里對應的MTU分別是1372+40 和 1420+40。同一個路徑上,MTU不一定是對稱的,也就是說A到B和B到A,兩條路徑上的MTU可以是不同的,對應的MSS也一樣。
三次握手中協商了MSS就不會改變了嗎?
當然不是,每次執行TCP發送消息的函數時,會重新計算一次MSS,再進行分段操作。
對端不傳MSS會怎么樣?
我們再看TCP的報頭。
TCP報頭其實MSS是作為可選項引入的,只不過一般情況下MSS都會傳,但是萬一遇到了哪臺機器的實現上比較調皮,不傳MSS這個可選項。那對端該怎么辦?
如果沒有接收到對端TCP的MSS,本端TCP默認采用MSS=536Byte。
那為什么會是536?
536(data)?+?20(tcp頭)+20(ip頭)=?576Byte前面提到了IP會切片,那會切片,也就會重組,而這個576正好是 IP 最小重組緩沖區的大小。
MTU是什么
MTU: Maximum Transmit Unit,最大傳輸單元。其實這個是由數據鏈路層提供,為了告訴上層IP層,自己的傳輸能力是多大。IP層就會根據它進行數據包切分。一般 MTU=1500 Byte。
假設IP層有 <= 1500 byte 需要發送,只需要一個 IP 包就可以完成發送任務;假設 IP 層有 > 1500 byte 數據需要發送,需要分片才能完成發送,分片后的 IP Header ID 相同,同時為了分片后能在接收端把切片組裝起來,還需要在分片后的IP包里加上各種信息。比如這個分片在原來的IP包里的偏移offset。
如何查看MTU
在mac控制臺輸入 ifconfig命令,可以看到MTU的值為多大。
$?ipconfig lo0:?flags=8049<UP,LOOPBACK,RUNNING,MULTICAST>?mtu?16384... en0:?flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST>?mtu?1500... p2p0:?flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST>?mtu?2304...可以看到這上面有好幾個MTU,可以簡單理解為每個網卡的處理能力不同,所以對應的MTU也不同。當然這個值是可以修改的,但不在今天的討論范疇內,不再展開。
在一臺機器的應用層到這臺機器的網卡,這條鏈路上,基本上可以保證,MSS < MTU。
MSS和MTU的區別為什么MTU一般是1500
這其實是由傳輸效率決定的。首先,雖然我們平時用的網絡感覺挺穩定的,但其實這是因為TCP在背地里做了各種重傳等保證了傳輸的可靠,其實背地里線路是動不動就丟包的,而越大的包,發生丟包的概率就越大。
那是不是包越小就越好?也不是
但是如果選擇一個比較小的長度,假設選擇MTU為300Byte,TCP payload = 300 - IP Header - TCP Header = 300 - 20 - 20 = 260 byte。那有效傳輸效率= 260 / 300 = 86%
而如果以太網長度為1500,那有效傳輸效率= 1460 / 1500 = 96% ,顯然比 86% 高多了。
所以,包越小越不容易丟包,包越大,傳輸效率又越高,因此權衡之下,選了1500。
為什么IP層會分片,TCP還要分段
由于本身IP層就會做分片這件事情。就算TCP不分段,到了IP層,數據包也會被分片,數據也能正常傳輸。
既然網絡層就會分片了,那么TCP為什么還要分段?是不是有些多此一舉?
假設有一份數據,較大,且在TCP層不分段,如果這份數據在發送的過程中出現丟包現象,TCP會發生重傳,那么重傳的就是這一大份數據(雖然IP層會把數據切分為MTU長度的N多個小包,但是TCP重傳的單位卻是那一大份數據)。
假設TCP不分段如果TCP把這份數據,分段為N個小于等于MSS長度的數據包,到了IP層后加上IP頭和TCP頭,還是小于MTU,那么IP層也不會再進行分包。此時在傳輸路上發生了丟包,那么TCP重傳的時候也只是重傳那一小部分的MSS段。效率會比TCP不分段時更高。
假設TCP分段類似的,傳輸層除了TCP外,還有UDP協議,但UDP本身不會分段,所以當數據量較大時,只能交給IP層去分片,然后傳到底層進行發送。
也就是說,正常情況下,在一臺機器的傳輸層到網絡層這條鏈路上,如果傳輸層對數據做了分段,那么IP層就不會再分片。如果傳輸層沒分段,那么IP層就可能會進行分片。
說白了,數據在TCP分段,就是為了在IP層不需要分片,同時發生重傳的時候只重傳分段后的小份數據。
TCP分段了,IP層就一定不會分片了嗎
上面提到了,在發送端,TCP分段后,IP層就不會再分片了。
但是整個傳輸鏈路中,可能還會有其他網絡層設備,而這些設備的MTU可能小于發送端的MTU。此時雖然數據包在發送端已經分段過了,但是在IP層就還會再分片一次。
如果鏈路上還有設備有更小的MTU,那么還會再分片,最后所有的分片都會在接收端處進行組裝。
IP分片再分片因此,就算TCP分段過后,在鏈路上的其他節點的IP層也是有可能再分片的,而且哪怕數據被第一次IP分片過了,也是有可能被其他機器的IP層進行二次、三次、四次….分片的。
IP層怎么做到不分片
上面提到的IP層在傳輸過程中因為各個節點間MTU可能不同,導致數據是可能被多次分片的。而且每次分片都要加上各種信息便于在接收端進行分片重組。那么IP層是否可以做到不分片?
如果有辦法知道整個鏈路上,最小的MTU是多少,并且以最小MTU長度發送數據,那么不管數據傳到哪個節點,都不會發生分片。
整個鏈路上,最小的MTU,就叫PMTU(path MTU)。
有一個獲得這個PMTU的方法,叫 Path MTU Discovery。
$cat?/proc/sys/net/ipv4/ip_no_pmtu_disc 0默認為0,意思是開啟PMTU發現的功能。現在一般機器上都是開啟的狀態。
原理比較簡單,首先我們先回去看下IP的數據報頭。
IP報頭DF這里有個標紅的標志位DF(Don't Fragment),當它置為1,意味著這個IP報文不分片。
當鏈路上某個路由器,收到了這個報文,當IP報文長度大于路由器的MTU時,路由器會看下這個IP報文的DF
如果為0(允許分片),就會分片并把分片后的數據傳到下一個路由器
如果為1,就會把數據丟棄,同時返回一個ICMP包給發送端,并告訴它"達咩!"數據不可達,需要分片,同時帶上當前機器的MTU
理解了上面的原理后,我們再看下PMTU發現是怎么實現的。
應用通過TCP正常發送消息,傳輸層TCP分段后,到網絡層加上IP頭,DF置為1,消息再到更底層執行發送
此時鏈路上有臺路由器由于各種原因MTU變小了
IP消息到這臺路由器了,路由器發現消息長度大于自己的MTU,且消息自帶DF不讓分片。就把消息丟棄。同時返回一個ICMP錯誤給發送端,同時帶上自己的MTU。
發送端收到這個ICMP消息,會更新自己的MTU,同時記錄到一個PMTU表中。
因為TCP的可靠性,會嘗試重傳這個消息,同時以這個新MTU值計算出MSS進行分段,此時新的IP包就可以順利被剛才的路由器轉發。
如果路徑上還有更小的MTU的路由器,那上面發生的事情還會再發生一次。
總結
數據在TCP分段,在IP層就不需要分片,同時發生重傳的時候只重傳分段后的小份數據
TCP分段時使用MSS,IP分片時使用MTU
MSS是通過MTU計算得到,在三次握手和發送消息時都有可能產生變化。
IP分片是不得已的行為,盡量不在IP層分片,尤其是鏈路上中間設備的IP分片。因此,在IPv6中已經禁止中間節點設備對IP報文進行分片,分片只能在鏈路的最開頭和最末尾兩端進行。
建立連接后,路徑上節點的MTU值改變時,可以通過PMTU發現更新發送端MTU的值。這種情況下,PMTU發現通過浪費N次發送機會來換取的PMTU,TCP因為有重傳可以保證可靠性,在UDP就相當于消息直接丟了。
文章推薦:
動圖圖解!GMP模型里為什么要有P?背后的原因讓人暖心
i/o timeout,希望你不要踩到這個net/http包的坑
妙啊! 程序猿的第一本互聯網黑話指南
程序員防猝死指南
我感覺,我可能要拿圖靈獎了。。。
給大家丟臉了,用了三年golang,我還是沒答對這道內存泄漏題
硬核!漫畫圖解HTTP知識點+面試題
TCP粘包 數據包:我只是犯了每個數據包都會犯的錯 |硬核圖解
硬核圖解!30張圖帶你搞懂!路由器,集線器,交換機,網橋,光貓有啥區別?
最后
畫動圖,太難了。。。看完求個贊,下次圖會動得更兇。
歡迎大家加我微信(公眾號里右下角“聯系我”),互相圍觀朋友圈砍一刀啥的哈哈。
如果文章對你有幫助,看下文章底部右下角,做點正能量的事情(點兩下)支持一下。(瘋狂暗示,拜托拜托,這對我真的很重要!)
我是小白,我們下期見。
別說了,一起在知識的海洋里嗆水吧
關注公眾號:【golang小白成長記】
總結
以上是生活随笔為你收集整理的动图图解!既然IP层会分片,为什么TCP层也还要分段?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【蓝桥杯C/C++】专题五:DFS深度优
- 下一篇: FreeSwitch连接讯时语音网关配置