上文對于網絡問題分析的還不夠精細,比如有時候網絡資源可能沒有達到瓶頸,并且沒有丟包產生,但這個時候網絡傳輸速率就是很慢或者是有丟包產生了,但是卻無法知道丟的是具體哪個包,沒有辦法知道整個tcp傳輸過程的具體情況,所以需要一種更加精細的去看網絡包傳遞過程的方法即抓包。
用wireshark去分析上傳文件整個tcp的傳輸過程,
用wireshark提供的高級的圖表功能去感受下tcp在整個過程當中所經歷的各種階段。
tcp在整個過程當中所經歷的階段:三次握手、慢啟動、擁塞避免、快速恢復、四次揮手。
除了三次握手、四次揮手,其他幾個階段都涉及到了tcp窗口的概念。
tcp的滑動窗口
tcp的發(fā)送和接收數據都是通過滑動窗口,在tcp協議里,發(fā)送數據的滑動窗口被稱為發(fā)送窗口;接收數據的滑動窗口,被叫做接收窗口。
發(fā)送窗口大小會影響發(fā)送數據的大小,那它的大小是受制于兩個因素:
第一個是對端接收窗口的大小,第二個是擁塞窗口的大小,取這兩個窗口大小的最小值。
tcp的發(fā)送窗口,如果只受限于對端接收窗口大小的話,這種情況在局域網其實是沒有什么問題的,但是在廣域網里,數據傳輸過程中很有可能是經過很多的路由器或者是交換機,如果中途設備網絡處理能力變差之后,即使對端接收窗口的能力很大,但是可能數據包在中途的時候就被丟掉了,所以tcp得有一種功能是能夠感知網絡的這種擁塞阻塞的情況,并根據這種情況的好壞,去調整發(fā)送窗口的大小,所以這個功能就是擁塞窗口的功能。
擁塞窗口是如何衡量這種擁塞情況的?
當發(fā)生重傳的時候,這個擁塞窗口就會認為此時的網絡發(fā)生了擁塞的情況。
重傳是分為快速重傳和超時重傳,擁塞窗口會根據這兩種重傳方式作出不同的策略。
超時重傳
指的是數據包在發(fā)送數據到對端的時候,本來是要接受到一個ack標志的數據包回來,但如果在一定時間以內發(fā)送端沒有接受到對端的ack包,那么發(fā)送端就會認為數據包丟掉了,那它會重新去傳遞相同的數據包到對端,這種相當于定期觸發(fā)的這種重傳,被稱為超時重傳。
快速重傳
當每次都要等到超時時再去重傳,會增大端與端的時延,所以快速重傳就認為如果是收到了對端三次重復的ack,那么就認為ack的這個包丟掉了,所以會馬上對丟掉的包去進行重傳,而不用等待定時器超時之后再去觸發(fā)重傳的動作。
對端重復ack是如何產生的呢? 因為tcp發(fā)包不是一個一個發(fā)的,而是一組一組的發(fā)到對端,所以其中如果某個包丟失了,那對端接收到的大于丟失包序號的包,那回應的ack將會是丟失前最大的ack序號。
比如發(fā)一組數據包到這個服務端,如果2號包丟失之后,那3、4、5號包還是有機會到達服務端的,
那服務端看到了3、4、5號包,但是又對比起自己的ack的序號,它發(fā)現還沒有對2號包去進行ack,所以它對3、4、5號包的這種ack的序號就是2,所以當連續(xù)的收到三個ack 2之后,那客戶端就會對2號包去進行一個重傳的動作,這就是快速重傳。
擁塞窗口對這兩個重傳是怎么進行不同策略的?
擁塞窗口它會經歷三個階段:慢啟動、擁塞避免、快速恢復 。
依次來看一下,當連接建立的時候,每次收到一個ack,那么擁塞窗口能夠去發(fā)送的最大mss(即一個tcp包的最大發(fā)送的字節(jié)數)就會翻倍,當這個最大發(fā)送的數據達到了慢啟動的閾值(這個是可以通過內核去配置的),就會進入第二個階段,叫作擁塞避免階段。在擁塞避免階段,擁塞窗口的增長速率,就沒有慢啟動階段那么快了,它的策略是每經過一個往返延遲,擁塞窗口就會增長mss的大小,無論是慢啟動還是擁塞避免階段,如果期間碰到重傳的這種行為的時候,那么擁塞窗口,會有選擇性的去進入慢啟動階段還是一個快速恢復階段。
快速恢復階段是為了避免每次碰到擁塞時都會進入慢啟動階段而產生的,因為每次如果重傳都進入到了慢啟動階段,那傳輸的效率會急劇的降低,所以快速恢復采用的策略是當遇到快速重傳的時候,讓擁塞窗口減半,然后它的mss的增長方式是和擁塞避免的增長方式是一樣的,采用低斜率的線性增長的方式。然后遇到超時重傳的時候,其實整個過程依然是會進入到慢啟動階段。這樣的話,就能根據不同的重傳,采用這種不同的縮小擁塞窗口的策略,來達到限制發(fā)送窗口的數據發(fā)送量的大小的目的。
在上傳文件的過程當中,
tcp發(fā)送數據的接收窗口、發(fā)送窗口是如何變化的?
這里使用了wireshark的tcp stream graphs這種高級圖表功能去看,首先來看一下慢啟動階段的一個特點,就是能夠讓發(fā)出去的包在特定時間以內能夠呈現指數級增長,因為每收到一個數據包的ack,它會讓擁塞窗口發(fā)出去的mss達到一個翻倍,所以它是一個指數級的增長。
這里用tcp的時序圖stevens來看這種增長變化,x軸是時間,y軸是發(fā)出去包的seq number,可以看到,在前面0-5秒,特別是接近五秒這段時間,它的一個seq number的增長速率是相當大的,也是十分陡峭的,然后在慢啟動之后,它的一個seq number其實是小于之前點的,那為什么會出現這樣的點?是因為發(fā)生了重傳,發(fā)生重傳就是發(fā)送之前發(fā)出去過的包,所以seq number會小于之前發(fā)送出去的seq number。并且在這個慢啟動之后,有好幾波這種低于之前發(fā)出去的seq number的情況,所以這里猜測:在第一次慢啟動之后,并且達到峰值之后,網絡出現了這種擁塞的情況,導致發(fā)生了這種重傳的現象,并且在后續(xù)的一段時間以內,這種重傳的現象還時不時的在發(fā)生,說明此時網絡真的是屬于一種很擁塞的情況,然后在7.5秒之后,網絡的情況就漸漸好轉了,然后丟包的現象就開始好起來了。在5.5到7.5秒的期間,的確是有很多這種超時重傳的現象,也驗證了剛剛的猜想。
在五秒的時候發(fā)生了一個擁塞,這里對上傳文件進行抓包,所以看一下這個擁塞到底是對傳輸速率有怎樣的影響?
在wireshark里提供了圖表的功能看整個過程的傳輸速率,
I/O Graphs就是看傳輸速率的圖表,以100毫秒的間隔去看整個過程傳輸數據的變化,可以看到,在發(fā)生擁塞之前,傳輸速率的增長依然是呈指數級別的增長,然后在發(fā)生重傳之后,速率在急劇的下降回來,下降到0之后,又開始呈現指數級別的增長,這里就和剛才所說的,碰到這種超時重傳之后,會進入一個慢啟動的階段,慢啟動階段的特點就是能讓發(fā)出去的包呈現一種指數級別增長。
第二次慢啟動階段所達到的峰值還沒有第一次那么高的時候,又下降,但是這個下降的坡度沒有第一次那么低,所以有可能這里是在進行一個快速恢復的階段。
快速恢復的階段碰到的是快速重傳,碰到快速重傳之后,傳輸數據就下降為之前的一半,然后進入一個快速恢復的階段,快速恢復階段,mss一直增長的,坡度是在一個固定斜率的直線,所以也和傳輸速率圖是極其吻合的,所以很有可能這里發(fā)生了一個快速重傳。
然后關于傳輸速率的影響,除了擁塞帶的影響 其實還有接收窗口所帶來的影響。
如果接收窗口太小了,傳輸速率是提升不上去的,那么如何肯定傳輸速率下降了是由于網絡的阻塞而不是接收窗口所帶來的影響呢?
其實有兩點,第一點在tcp的時序圖stevens找到了對應速率下降的時間點,并且看對應時間點包的傳輸情況是有發(fā)生大量超時重傳的,說明的確有丟包產生,說明當時的網絡狀況并不好。
第二點通過wireshark和window scaling去看接收窗口隨時間的一個變化情況,
可以看到綠色這條線是代表的接收窗口的大小,藍色這種bytes out就是經常說的在途數據,在途數據是指已經發(fā)送但是還未被確認的數據,在途數據越多,說明發(fā)送端發(fā)送的數據就越多。
整個過程,可以看到接收窗口其實是在不斷增長的,并且增長到有4M之后,沒有變小,一直是穩(wěn)定的持續(xù)到4M這個界限,然后反而是發(fā)送端的一個在途數據在達到峰值之后,就一直變小,并且在后續(xù)的這段抓包時間內,都沒有達到接收窗口的飽和,所以斷定了這里傳輸速率的下降其實是由于當時網絡真的是不好,不是由于接收窗口這種大小導致的。
那如果是由于接收窗口大小導致的,那這個window sacaling所展現出來圖應該是怎么樣的?
Site Out比較貼合4M的這個直線,而不是中間留了那么一大片空白的地方,并且在wireshark中可以看到很多tcp window full這種包,就說明你的接收窗口可能是一直處于一種飽和的狀態(tài),有可能接收窗口太小了,這個時候得調大接收窗口的數量。
調整接收窗口的大小的策略
接收窗口受tcp里面的receive buffer大小的影響,reveive buffer是由內核去根據系統可用內存的情況和內核參?.NET.ipv4.tcp_rmem參數動態(tài)的去調節(jié),tcp的receive buffer的大小,不完全的等于接收窗口的大小,是用reveive buffer的大小去除以2的指數次方的大小分配給應用,這個參數也是可以去通過內核文件去改配置的。
就比如這個scale的大小是2的時候,那就會有1/4的receive buffer是給應用的,然后其余的3/4才會真的用于接收窗口。
如果表達tcp窗口的大小值,在tcp協議報文中留意window字段,在抓到包里面可以看到,window字段的表達式其實只有16位,16位就決定了要表達這個窗口的最大值,只能表示位64kb,那如果tcp窗口大于64kb的時候 ,就要用另外一個字段window scaling,這個字段能夠去和window這個字段去進行乘積,同樣的去通過2的scale次方進行一個乘積,然后得到的最終結果,才是實際的接收窗口的大小。
window scaling字段會在握手的時候就告訴給對方,如果是接收窗口是瓶頸的話,那可能要調大接收方的receive buffer或者是tcp window scaling的參數。
本文介紹了tcp stream graphs宏觀的分析了文件上傳過程當中tcp的傳輸過程,用的是wireshark里面提供這種圖表的功能。






