动手写一个探测网络质量(丢包率/RTT/队形等)的工具
說(shuō)到網(wǎng)絡(luò)技術(shù),我個(gè)人比較關(guān)注IP,其次是鏈路設(shè)備,然后才是TCP,這可能跟我第一次接觸網(wǎng)絡(luò)技術(shù)時(shí)所遇到的公司有關(guān),它們是華為3Com以及Cisco,而不是Google,Yahoo或者BAT。
??????? 然而能接觸到的大多數(shù)的人可能更關(guān)注的是TCP,因?yàn)檫@是他們唯一能接觸到的網(wǎng)絡(luò)技術(shù),雖然在俠義上,TCP并不屬于網(wǎng)絡(luò),它是典型的一個(gè)端到端系統(tǒng),網(wǎng)絡(luò)只是它經(jīng)由的一個(gè)路徑。
??????? 以公共交通系統(tǒng)為例,TCP比較類似票務(wù)和調(diào)度系統(tǒng),它關(guān)注是否可以賣票,可以賣多少票,始發(fā)站是否可以發(fā)車,間隔多久發(fā)一班等等這種事情。至于車在途徑道路上發(fā)生了什么,甚至途徑哪些道路,票務(wù)系統(tǒng)并不關(guān)心,偶爾,小概率的,坐在公交總站的阿姨會(huì)接到電話,路上司機(jī)打來(lái)的,匯報(bào)一些突發(fā)情況,車壞了,車翻了,自己被捅了一刀...然后阿姨唯一能做的就是再派一輛車過(guò)去,她既無(wú)力修車,也無(wú)力修路,更無(wú)力(事實(shí)上也無(wú)權(quán))懲罰歹徒...
...
??????? 令人悲哀的痛點(diǎn)在于,一個(gè)承諾質(zhì)量的端到端系統(tǒng)跑在一個(gè)根本無(wú)法保證質(zhì)量的統(tǒng)計(jì)復(fù)用的分組交換網(wǎng)絡(luò)里面!請(qǐng)注意,公路也是統(tǒng)計(jì)復(fù)用的分組交換網(wǎng)路。
???????? 整個(gè)網(wǎng)絡(luò)是擁塞的,而且隨時(shí)都可能擁塞,如果我們想設(shè)計(jì)一個(gè)好的TCP,我們就必須能動(dòng)態(tài)適應(yīng)網(wǎng)絡(luò)的當(dāng)前狀況,我們必須能夠探知網(wǎng)絡(luò)的當(dāng)前狀況,因此我們需要一個(gè)工具。
??????? 在給出這個(gè)工具之前,我們先從Wireshark說(shuō)起。
1.如何看Wireshark里TCP trace圖
用Wireshark打開一個(gè)保存TCP流的pcap文件,點(diǎn)擊“統(tǒng)計(jì)”-“TCP流圖形”-“時(shí)間序列(tcptrace)”,我們可以看到一張圖表,通過(guò)該圖表可以探知關(guān)于網(wǎng)絡(luò)狀況的大部分細(xì)節(jié):??????? 因此,我需要一個(gè)比較客觀的工具,也就是說(shuō)它對(duì)網(wǎng)絡(luò)狀態(tài)是無(wú)知的,也不奢望獲知網(wǎng)絡(luò)狀態(tài),它只是按照自己的參數(shù)固定速率發(fā)送數(shù)據(jù),然后我們通過(guò)回應(yīng)來(lái)探測(cè)網(wǎng)絡(luò)到底發(fā)生了什么。
2.基于數(shù)據(jù)包守恒的ping -f
在寫工具之前,首先看看現(xiàn)成的ping -f是否好用。??????? 這個(gè)工具自己試一下便知,不多談。事實(shí)上,它是基于數(shù)據(jù)包守恒的,即收到了n個(gè)Reply,發(fā)出去n個(gè)Request,因此它會(huì)根據(jù)網(wǎng)絡(luò)的狀態(tài)自動(dòng)調(diào)速,但是它計(jì)算的是一種保守狀態(tài)的丟包率,
比如下面的序列:
1).發(fā)出100個(gè)Request,收到100個(gè)Reply。
2).發(fā)出100個(gè)Request,收到80個(gè)Reply,假設(shè)此時(shí)真的發(fā)生了擁塞丟包。
3).發(fā)出80個(gè)Request,收到80個(gè)Reply,假設(shè)擁塞馬上緩解了。
4).請(qǐng)問(wèn)怎么可以快速獲知擁塞緩解了??
因此我更希望的是,固定數(shù)量發(fā)出數(shù)據(jù)包,然后看回應(yīng):
1).發(fā)出100個(gè)Request,收到100個(gè)Reply。
2).發(fā)出100個(gè)Request,收到80個(gè)Reply,假設(shè)此時(shí)真的發(fā)生了擁塞丟包。
3).發(fā)出100個(gè)Request,收到100個(gè)Reply,擁塞緩解馬上被探知。
3.我的Python工具概述
在上面的篇幅,我給出了不用Wireshark和ping -f的理由:不用Wireshark的理由:
它完全是TCP實(shí)現(xiàn)的行為勾畫,在實(shí)現(xiàn)的很垃圾的TCP中,根本無(wú)法勾勒網(wǎng)絡(luò)的狀態(tài),即便是在完美的TCP實(shí)現(xiàn)中,它表示的也是TCP如何反應(yīng)網(wǎng)絡(luò)狀況變化的,這個(gè)信息絲毫沒有指導(dǎo)意義,我們也很難知道網(wǎng)絡(luò)到底發(fā)生了什么。總之,不同的TCP實(shí)現(xiàn)針對(duì)相同的網(wǎng)絡(luò)質(zhì)量會(huì)給出不同的數(shù)據(jù),畫出不同的圖,而且,它太復(fù)雜了!不用ping -f的理由:
很直接的說(shuō),ping -f跟tcptrace沒有什么根本的不同,只是它的“擁塞控制”機(jī)制更加簡(jiǎn)單,完全就是根據(jù)數(shù)據(jù)包守恒來(lái)的。不多說(shuō)了。??????? 我寫這個(gè)工具完全是為了自用,而且確實(shí)也還可以,這個(gè)工具可以完成以下的功能:
1).探測(cè)丟包率
這種丟包率是客觀的丟包率,包括噪聲和排隊(duì)造成的丟包。我基于ping -f修改了,把根據(jù)Reply的調(diào)速反饋機(jī)制去掉了,因?yàn)檫@樣可以探測(cè)出更加詳細(xì)的隊(duì)形情況以及擁塞對(duì)丟包的影響。不然的話,如果使用ping -f,你不得不通過(guò)斜率來(lái)觀測(cè)這種影響,而且當(dāng)擁塞恢復(fù),探測(cè)端無(wú)法及時(shí)感知。??????? 我在這里就不貼圖了,基本就跟ping -f是一樣的。
2).探測(cè)RTT波動(dòng)
該工具最終會(huì)生成一個(gè)文件,該文件的截圖如下:3).探測(cè)隊(duì)列的隊(duì)形
和ping -f不同,我的工具可以生成點(diǎn)星文件("."表示收到了回應(yīng),"*"表示發(fā)送了數(shù)據(jù)),從點(diǎn)和星的分布,我們可以更加深入的探測(cè)隊(duì)列細(xì)節(jié)。如下只是一個(gè)例子:動(dòng)態(tài)的圖跟ping -f 幾乎一樣:
??????? 以RED隊(duì)列為例,丟包往往是緩速隨機(jī)的,然后如果擁塞不緩解,丟包就會(huì)趨于連續(xù),表現(xiàn)為星號(hào)越來(lái)越連續(xù)且增多,點(diǎn)號(hào)的連續(xù)性則相反,趨于離散化...我的工具不重傳,只觀測(cè),所以完全可以通過(guò)點(diǎn)星的分布來(lái)解析隊(duì)列的細(xì)節(jié),并且很有可能你能把路徑上是否有UDP流氓找出來(lái)!即便你無(wú)法控制你的路徑從而繞開,不也是可以反制一下么?你可以通過(guò)點(diǎn)星分布的變化情況得知擁塞是否由于發(fā)送端主動(dòng)降速而緩解。
4.如何使用這個(gè)工具
首先,你不能盲目的去探測(cè),你首先要有一個(gè)大概的拓?fù)洹1热?#xff0c;還是baidu,我們想知道到達(dá)baidu的路徑中的情況,利用上古神奇traceroute,我們可以得到很多信息:
為什么用Windows的tracert?因?yàn)槲业奶摂M機(jī)是NAT模式,特殊原因不能用Bridge,所以我用Windows...
得到了路徑細(xì)節(jié),我們可以逐步探測(cè)各個(gè)節(jié)點(diǎn)了,如下這樣:./ic.py 183.56.64.62 1 1000 1
./ic.py 14.29.121.206 1 1000 1
...
由于我程序的時(shí)間精度不夠,很難探知更詳細(xì)的信息,但是這個(gè)問(wèn)題是可以10秒內(nèi)解決的...為什么不解決,是因?yàn)槲矣X得這已經(jīng)夠了。
5.說(shuō)到最后,代碼呢?
前面扯了那么多,代碼呢?代碼在 github??????? 但是這里也貼一份吧:
#!/usr/local/bin/pythonimport sys import time from time import sleep,ctimeimport signal import threading from scapy.all import *# 指定目標(biāo)IP地址 target = sys.argv[1] # 指定執(zhí)行次數(shù) tot = int(sys.argv[2]) # 指定每次發(fā)送的包量 tot_per = int(sys.argv[3]) # 指定是否回顯 vl = int(sys.argv[4]) flt = "host " + target + " and icmp"handle = open("/dev/null", 'w')out_list = [] in_list = []def output():all = out_list + in_listall.sort(lambda x,y:cmp(x[3],y[3]))for item in all:print item[0], item[1], item[2], item[3]*10sys.stdout.flush()os._exit(0)def signal_handler(signal, frame):output()class ThreadWraper(threading.Thread):def __init__(self,func,args,name=''):threading.Thread.__init__(self)self.name=nameself.func=funcself.args=argsdef run(self):apply(self.func,self.args)# 將結(jié)果輸出到list def printrecv(pktdata):if ICMP in pktdata and pktdata[ICMP]:seq = str(pktdata[ICMP].seq)if seq == tot_per + 2:returnif str(pktdata[IP].dst) == target:handle.write('*')handle.flush()out_list.append(('+', 1, seq, time.clock()))else:if vl == 2:handle.write('.')else:handle.write('\b \b')handle.flush()in_list.append(('-', 0, seq, time.clock()))# 收到seq+2的包就停止抓包并終止程序 def checkstop(pktdata):if ICMP in pktdata and pktdata[ICMP]:seq = str(pktdata[ICMP].seq)if int(seq) == tot_per + 2 and str(pktdata[IP].src) == target:handle.write("\nExit:" + ctime() + '\n')output()return Truereturn False# 發(fā)送線程 def send_packet():times = 0while times < tot:times += 1send(IP(dst = target)/ICMP(seq = (0, tot_per))/"test", verbose = 0, loop = 1, count = 1)send(IP(dst = target)/ICMP(seq = tot_per+2)/"bye", verbose = 0)# 接收線程 def recv_packet():sniff(prn = printrecv, store = 1, filter = flt, stop_filter = checkstop)def startup():handle.write("Start:" + ctime() + '\n')send_thread = ThreadWraper(send_packet,(),send_packet.__name__)send_thread.setDaemon(True) send_thread.start()recv_thread = ThreadWraper(recv_packet,(),recv_packet.__name__)recv_thread.setDaemon(True) recv_thread.start()signal.pause()if __name__ == '__main__':if vl != 0:handle.close()handle = sys.stderrsignal.signal(signal.SIGINT, signal_handler)startup()
這個(gè)代碼寫的不好,因?yàn)槲艺娴牟辉趺磿?huì)編程。
??????? 這個(gè)代碼使用了令人陶醉的scapy!這個(gè)我5年前就接觸過(guò)的東西直到今天才用起來(lái)。關(guān)于scapy的文檔,我覺得比較好的是 這個(gè)。
本文最后,再舉一個(gè)例子來(lái)聊一下TCP
蝙蝠是個(gè)瞎子,TCP也是個(gè)瞎子。蝙蝠雖瞎但不會(huì)撞墻,依靠的是聲波定位,而TCP雖瞎也能保持平滑發(fā)送,依靠的是ACK時(shí)鐘流。而這個(gè)引發(fā)了另一個(gè)思路,其效果就是,我們可以采取一些措施撞死蝙蝠。??????? TCP和蝙蝠不同的是,蝙蝠發(fā)出的聲波無(wú)法被緩存,它永遠(yuǎn)直來(lái)直去,而TCP發(fā)出的數(shù)據(jù)包卻可以被中間的網(wǎng)絡(luò)設(shè)備緩存很久,因此,TCP測(cè)出的RTT除以2并不一定是數(shù)據(jù)包到達(dá)接收端的時(shí)間!如果你不理解我的意思,請(qǐng)考慮以下場(chǎng)景:
0).發(fā)送端到接收端的路徑,路徑傳輸用時(shí)為10秒,因此不考慮緩存,來(lái)回為20秒;
1).時(shí)間點(diǎn)A,數(shù)據(jù)包P發(fā)出;
2).到達(dá)接收端前,經(jīng)過(guò)了5秒,數(shù)據(jù)包P在路徑正中間被一個(gè)設(shè)備緩存了10秒;
3).在時(shí)間點(diǎn)A的15秒后數(shù)據(jù)包P繼續(xù)前行,又過(guò)了5秒,到達(dá)接收端;
4).接收端對(duì)數(shù)據(jù)包P的ACK未緩存經(jīng)過(guò)了10秒返回到發(fā)送端。
經(jīng)過(guò)計(jì)算得到時(shí)間點(diǎn)A發(fā)送的數(shù)據(jù)包P的RTT為5+10+5+10=30秒,除以2就是15秒,請(qǐng)問(wèn)15秒是發(fā)送端到達(dá)接收端的時(shí)間嗎?顯然不是!如果TCP不是瞎子,那么它肯定知道上述的過(guò)程,然而TCP是個(gè)瞎子,所以它不知道上面的具體過(guò)程。如果數(shù)據(jù)包P沒有被緩存,顯然只需要10秒就能到達(dá)接收端,然而現(xiàn)在卻算出來(lái)是15秒,這多出來(lái)的5秒發(fā)生了什么?TCP不知道,因此傳統(tǒng)上,TCP會(huì)認(rèn)為發(fā)生了擁塞排隊(duì)。
??????? 但是對(duì)于擁塞排隊(duì)的細(xì)節(jié),TCP真的很容易判斷清楚嗎?
??????? TCP會(huì)把RTT的陡增視為發(fā)生了擁塞,但是:
A.如果排隊(duì)發(fā)生在數(shù)據(jù)包P到達(dá)接收端的過(guò)程中,數(shù)據(jù)包到達(dá)接收端的總時(shí)間為20秒,RTT/2=15秒,此時(shí)TCP少算了5秒;
B.如果排隊(duì)發(fā)生在ACK返回到發(fā)送端的過(guò)程中,數(shù)據(jù)包到達(dá)接收端的總時(shí)間為10秒,RTT/2=15秒,此時(shí)TCP多算了5秒。
B-1.如果ACK發(fā)生了擁塞排隊(duì)或者丟棄,后面的ACK會(huì)連帶著代替該ACK去確認(rèn)已經(jīng)發(fā)送的數(shù)據(jù),TCP對(duì)待數(shù)據(jù)包P和ACK是不同的!
因此,RTT陡增(并且持續(xù)保持高值)可以比較容易定性擁塞的發(fā)生,卻很難從RTT抖動(dòng)情況去辨析擁塞發(fā)生的細(xì)節(jié),就更別說(shuō)去區(qū)分偶然的噪聲丟包還是擁塞丟包了...
于是回聲定位技術(shù)給了我一些思路,這個(gè)思路其實(shí)也想了蠻久了。我們可以從如何發(fā)現(xiàn)中間網(wǎng)絡(luò)設(shè)備的流量整形說(shuō)起...
寫在最后
雖然我是一個(gè)典型的IP粉,但我絲毫沒有貶低TCP的意思,事實(shí)上我也不常接觸到中間網(wǎng)絡(luò)的設(shè)備,我之所以總是對(duì)TCP表現(xiàn)的不屑一顧是因?yàn)槲铱偸前l(fā)現(xiàn)很多所謂的“精通網(wǎng)絡(luò)編程”的人事實(shí)上只是“精通編程”而根本不懂網(wǎng)絡(luò),對(duì)此的另一個(gè)極端,是我在2013到2014年的時(shí)候碰到的一些CCIE,他們真的十分精通網(wǎng)絡(luò),但是卻絲毫不懂socket編程,也不懂協(xié)議棧的實(shí)現(xiàn),而我在多年的時(shí)間內(nèi)試圖調(diào)和兩者。說(shuō)實(shí)話,我鄙視過(guò)那些玩弄網(wǎng)絡(luò)設(shè)備而不懂編程的人,也鄙視過(guò)那些在端主機(jī)編程但是不懂網(wǎng)絡(luò)的人,但其實(shí)我更鄙視的是我自己。
??????? tcpdump/Wireshark/tshark抓包工具是利器,但是絕對(duì)不是唯一的,碰到問(wèn)題僅僅依賴分析數(shù)據(jù)包的人事實(shí)上很大的可能是他曾經(jīng)從事過(guò)很長(zhǎng)一段時(shí)間協(xié)議分析和逆向的工作,在這段時(shí)間形成了自己的工作方式,如果是一個(gè)從事路由器交換機(jī)工作的人,他可能就會(huì)依賴另外一種工具了。這就是網(wǎng)絡(luò)的復(fù)雜性之體現(xiàn)。
總結(jié)
以上是生活随笔為你收集整理的动手写一个探测网络质量(丢包率/RTT/队形等)的工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Unity自定义文件夹图标颜色 个性化U
- 下一篇: IAP操作系统升级