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