在解決日常的支持需求中,經(jīng)常會遇到一些用戶反饋一些無法簡單復現(xiàn)的bug,有很大一部分的bug是由于用戶自身的網(wǎng)絡環(huán)境波動,或者是本身網(wǎng)絡環(huán)境就較為惡劣,而服務在面對這種惡劣的網(wǎng)絡環(huán)境的健壯性不夠,導致會出現(xiàn)一些意想不到的bug。而在正常的開發(fā)自測過程中很難去營造出這種惡劣的網(wǎng)絡環(huán)境,使得這些bug較難被提前發(fā)現(xiàn)和修復。另外一些服務在惡劣網(wǎng)絡環(huán)境下雖然不會出現(xiàn)不可用的情況,但是用戶體檢很差,為了優(yōu)化這個情況下的用戶體驗,也需要去在本地模擬這種環(huán)境來進行調(diào)優(yōu)。
所以要去復現(xiàn)這些bug,甚至是去提前發(fā)現(xiàn)這些bug,就需要能夠在開發(fā)環(huán)境中模擬出惡劣的網(wǎng)絡環(huán)境,從而看到在這種惡劣的網(wǎng)絡環(huán)境下的服務的表現(xiàn)等。當前模擬惡劣網(wǎng)絡環(huán)境主要可以通過以下這些手段實現(xiàn):
- 通過應用層或者傳輸層的代理服務器,通過在代理服務器上設置一些模擬惡劣網(wǎng)絡環(huán)境的參數(shù),使得通過這些代理服務器的流量都被轉化為惡劣網(wǎng)絡環(huán)境下的流量。如利用Fiddler,Charles等具有代理服務器功能的網(wǎng)絡流量分析軟件來實現(xiàn)。
- 通過利用一些更底層的驅動層面的服務,通過控制網(wǎng)卡的收包發(fā)包的行為,來模擬惡劣的網(wǎng)絡環(huán)境。如dummynet的ipfw驅動等。
- 通過建立一個可控的網(wǎng)關,在網(wǎng)關上部署模擬惡劣環(huán)境的相關程序,所有需要借助該網(wǎng)關進行轉發(fā)的流量都會被模擬為惡劣網(wǎng)絡條件。Linux下的netem就提供了這類支持。
這里主要先講的是第一種手段,即利用Fiddler來模擬惡劣的網(wǎng)絡環(huán)境,對服務進行測試,這個手段實現(xiàn)簡單,較為直觀,但是缺點是只能支持那些利用HTTP進行通信和交互的服務。在之后的文章中也會進一步說一下后兩種手段。
【Fiddler是啥】
Fiddler的官網(wǎng)上是這樣描述它自己的:The free web debugging proxy for any browser, system or platform,即跨瀏覽器、跨系統(tǒng)、跨平臺的免費Web Debug代理服務器。當你的HTTP瀏覽經(jīng)過Fiddler時,F(xiàn)iddler可以監(jiān)視流量,查看HTTP通訊的各種信息,設置斷點查看和修改HTTP數(shù)據(jù),甚至可以構造各種測試用的HTTP包以及重放已記錄的包等。其官網(wǎng)是http://www.fiddler2.com/fiddler2/,上面詳細地介紹了Fiddler到底是什么。
【簡單地利用Fiddler限速模擬惡劣網(wǎng)絡環(huán)境】
Fiddler本身已經(jīng)預置提供了模擬Modem速度的選項,其位置位于:
Rules – Performances – Simulate Modem Speeds
勾選該選項后,所有通過Fiddler代理的流量都會變得和多年前的56k小貓時上網(wǎng)一般的慢。
由于Fiddler只是一個HTTP代理,要直觀地看出限速效果,最好是運行在瀏覽器中的測速工具,這里選用speedtest.net提供的測速工具進行測試。
首先是開啟該選項之前的速度:
打開了Simulate Modem Speeds后:

速度已經(jīng)回到了當年那種無法忍受的低速了,注意到這里PING值也有了顯著的提高,而事實上ping值是ICMP層的控制報文,并不會被Fiddler影響,理論上ping值并不會出現(xiàn)提高的情況,進一步分析Fiddler中的報文則可以看出端倪:

事實上網(wǎng)頁插件并不能實現(xiàn)發(fā)送ICMP包并得到ping值的功能,而是用多次較小的HTTP GET請求的響應時間來計算PING值,這里實際算出來的是一個平均的HTTP的RTT值,所以受到Fiddler模擬惡劣環(huán)境的影響就是正常的了。
【調(diào)整模擬惡劣網(wǎng)絡環(huán)境的參數(shù)】
直接模擬Modem速度實在是慢爆了,事實上就算是在很差信號的情況下,手機移動網(wǎng)絡的速度都已經(jīng)超過了當年的56k Modem速度了,所以采用默認的配置模擬出來的環(huán)境過于惡劣,并不一定符合需求,此時就需要對限速的參數(shù)進行調(diào)整。
Fiddler本身就提供了一個配置文件供調(diào)整這些參數(shù),點擊:
Rules – Customize Rules…
就會用文本編輯器打開CustomRules.js文件,其默認位于用戶目錄的文檔目錄下的\Fiddler2\Scripts 位置,后綴名是js,其內(nèi)容實質是JScript.NET——微軟對ECMAScript規(guī)范的實現(xiàn),與日常使用的javascript是屬于同一個規(guī)范下的,但是在擴展的細節(jié)實現(xiàn)存在一定的不同。
打開該文件后,可以找到一個m_SimulateModem標志位:
if (m_SimulateModem) {
- 該標志位控制著oSession的兩個參數(shù)值的設置,當勾選了Simulate Modem Speeds時,request-trickle-delay與response-trickle-delay就會被設置,其中request-trickle-delay中的值代表每KB的數(shù)據(jù)被上傳時會被延時多少毫秒,response-trickle-delay則對應下載時每KB的數(shù)據(jù)會被延時多少毫秒,如果本身網(wǎng)速已經(jīng)相當快的話,這里設置的值就可以近似地推算出開啟模擬后的上傳和下載帶寬了,比如默認設置下下載延時為150ms,上傳延時為300ms,對應可以推算出大致的模擬帶寬為:
上傳帶寬=(1*8/1000)/0.300≈0.053Mbps
下載帶寬=(1*8/1000)/0.150≈0.027Mbps
然而實際情況下卻得到了兩倍于這個值的帶寬,推測可能是Fiddler的內(nèi)部實現(xiàn)上有一些和描述上的不同,為何為造成這個現(xiàn)象現(xiàn)在還不是很清楚,所以上述公式最后還需要修正一個2.0的系數(shù),即:
上傳帶寬=((1*8/1000)/0.300)*2.0≈0.106Mbps
下載帶寬=((1*8/1000)/0.150)*2.0≈0.053Mbps
假設我們將兩個參數(shù)都設置為50,則會得到上下載帶寬均為0.32Mbps,測速結果如下所示:

【編寫自定義腳本】
進一步地,我們可以擴展CustomRules.js里的邏輯,參照Jscript的文檔可以在模擬惡劣環(huán)境中加入更多自定義的邏輯,這里實現(xiàn)了一個隨機延時量設置,使得網(wǎng)絡帶寬不是恒定為一個低速的值,而是會在一定范圍內(nèi)隨機抖動:
static function randInt(min, max) {
return Math.round(Math.random()*(max-min)+min);
}
if (m_SimulateModem) {
得到的測試結果如下:
在測速過程中的瞬時速度的趨勢圖如下:
可以看到整體的網(wǎng)絡限速存在了一定程度的抖動。
通過進一步擴展CustionRules.js可以實現(xiàn)很多需要的惡劣環(huán)境模擬場景,如果場景較為復雜的話,也可以通過編寫Fiddler的插件的方式,編寫C#插件代碼來進一步控制Fiddler的行為,在這里就不多做贅述了。詳細可以參照:http://docs.telerik.com/fiddler/extend-fiddler/extendwithdotnet
【Fiddler模擬惡劣網(wǎng)絡環(huán)境的局限性】
Fiddler進行限速較為簡單和靈活,配置也較為方便,但是由于它是一個應用層的HTTP的代理,只能模擬該層上的行為,對于一些復雜的網(wǎng)絡層的丟包、重傳等惡劣情況就不能很好的模擬出來,而且對于其他協(xié)議的應用也不支持,后續(xù)會介紹一些其他的模擬惡劣環(huán)境的方法和軟件來彌補這些缺失。
利用Dummynet模擬惡劣網(wǎng)絡環(huán)境
在之前的文章中提到了三種模擬惡劣網(wǎng)絡環(huán)境調(diào)試代碼的手段:
- 應用層或者傳輸層的代理服務器
- 傳輸層或者網(wǎng)絡層控制數(shù)據(jù)包的驅動
- 網(wǎng)絡層控制數(shù)據(jù)包的網(wǎng)關
同時在之前的文章中介紹了第一種手段,即利用應用層的HTTP代理Fiddler來模擬惡劣網(wǎng)絡環(huán)境,這種方式簡單且靈活,但是其處于應用層,限制較大,同時也沒有辦法從帶寬和延時兩個方面分別去精細化地對惡劣網(wǎng)絡環(huán)境進行模擬,這里介紹第二種手段——Dummynet。
Dummynet簡介
Dummynet的官網(wǎng)地址是:http://info.iet.unipi.it/~luigi/dummynet/,官網(wǎng)上對于dummynet的描述是這樣的:dummynet is a live network emulation tool, originally designed for testing networking protocols, and since then used for a variety of applications including bandwidth management. 即Dummynet是一個實時的網(wǎng)絡模擬工具,事實上dummynet是ipfw防火墻的一部分,ipfw是一個網(wǎng)絡層的防火墻,并內(nèi)建于FreeBSD之中。
利用ipfw的options DUMMYNET選項,可以設置一系列的pipes,從而做到對網(wǎng)絡流量進行控制的目的。
Dummynet基本原理
如官網(wǎng)上提供的這張圖所示,dummynet通過在兩個網(wǎng)絡層中(一般是在傳輸層和應用層之間或者網(wǎng)絡層與傳輸層之間)建立一條條pipe的方式來控制網(wǎng)絡流量,符合設定的規(guī)則中的網(wǎng)絡流量會被引入這些管道中去,從而使得dummynet可以介入這些流量之中,控制帶寬、延遲,甚至進一步控制丟包率等眾多參數(shù)。
Dummynet安裝(Windows)
Dummynet本身作為ipfw防火墻的一部分,其內(nèi)建于FreeBSD,其本身支持FreeBSD,OSX,Linux,Windows多種操作系統(tǒng)下的安裝和使用,在OSX和Linux下下載源碼編譯安裝一般即可使用,這里主要介紹一下Windows下的Dummynet安裝。
Dummynet在Windows下是作為一個網(wǎng)卡上的服務驅動存在的,在官方給出的二進制文件包中已經(jīng)包含了該驅動的sys與inf文件,但是該驅動是沒有包含數(shù)字簽名的,在并沒有引入驅動強制簽名機制的Windows操作系統(tǒng)(如Windows XP)上時,直接到官網(wǎng)下載到最新的二進制包,然后依照之后的操作步驟進行安裝即可以完成ipfw+dummynet服務驅動的安裝,但是在引入了驅動強制簽名機制(一般是Windows 7以后的Windows版本,或者設置了比較嚴格的組策略)的操作系統(tǒng)上,是無法安裝ipfw+dummynet服務驅動的,要解決這個問題可以使用兩種方案:
- 打開Windows的測試模式,關閉驅動強制簽名機制,然后安裝無簽名的驅動
這種方式的實現(xiàn)也有兩種方式,一種是在啟動Windows時啟動選項中關閉驅動強制簽名校驗機制,可以參考這篇文章 http://jingyan.baidu.com/article/7c6fb42879543380642c9036.html ,另外一種方式則是進入Windows的測試模式,可以參考 http://jingyan.baidu.com/article/acf728fd21c3e7f8e510a3ef.html 文章中的說明。 - 為無簽名的ipfw+dummynet服務驅動打上自簽名或者可用的簽名
如何進行自簽名或者打上可用的簽名可以參考MSDN上的說明 https://msdn.microsoft.com/en-us/library/windows/hardware/ff544865(v=vs.85).aspx ,具體如何簽名并不在本文的范疇內(nèi)。
本文主要使用第二種方式,為ipfw+dummynet服務驅動打上百度簽名后,即可在Windows 7上正常安裝。
安裝的方式如下:
- 找到你需要限制帶寬和流量的網(wǎng)卡,進入屬性:

- 點擊下方的安裝,選擇服務,然后選擇從磁盤安裝,并在瀏覽中找到ipfw+dummynet服務驅動的inf安裝文件:

- 在“此連接使用下列項目”中看到ipfw+dummynet之后,即代表ipfw+dummynet服務驅動安裝成功,如果安裝成功后遇到網(wǎng)卡無法使用的情況,一般會是前一次卸載沒有卸載完全,重啟電腦可以解決。
安裝完成后可以以管理員權限運行binary目錄下的testme.bat,如果運行結果中沒有報警信息或者錯誤信息,則驅動安裝是成功的:

利用Dummynet模擬惡劣網(wǎng)絡環(huán)境
利用dummynet的pipe,可以設置一些特定的規(guī)則,就可以達到模擬惡劣網(wǎng)絡環(huán)境的目的,且這些設置對于在操作系統(tǒng)中運行的應用程序來說是透明的、自動生效的,不需要像在使用Fiddler來進行模擬時還需要設置http代理。
- 低帶寬模擬
惡劣網(wǎng)絡環(huán)境最基礎的模擬方式就是模擬較小帶寬下的情況,利用dummynet中的pipe設置,可以將所有的tcp流和udp流的帶寬限制在一定的級別上,運行一個管理員的命令行,并利用binary目錄下的ipfw.exe文件就可以設置dummynet中的pipe,在命令行中執(zhí)行以下命令:
ipfw add pipe 2 in proto tcp
ipfw pipe 2 config bw 2Mbit/s
即可將tcp的下行流量限制在2Mbit/s的帶寬上,這里采用和上一篇文章中采用的相同的測速網(wǎng)站speedtest.net來驗證限制帶寬的效果,在執(zhí)行以下指令以前的測速結果如下:

在執(zhí)行完上述指令以后的測速結果如下:

- 高延時模擬
在管理員權限的命令行中執(zhí)行以下命令:
ipfw add pipe 10 ip from any to any
ipfw pipe 10 config delay 200
這會使得所有的包都被延時200ms,這里利用ping命令來ping百度的方式來直接測試延時,命令執(zhí)行以前結果如下:

命令執(zhí)行以后結果如下:

- 高丟包率模擬
在管理員權限的命令行中執(zhí)行以下命令:
ipfw add pipe 10 ip from any to any
ipfw pipe 10 config plr 0.1
這里設置的是10%的丟包率,這里利用ping -t指令繼續(xù)ping百度,來觀察丟包率的變化:
執(zhí)行以前:
執(zhí)行以后:

- 更加精細化的設置
可以看到相比Fiddler來說,利用dummynet可以更加精細化地從各個角度去設置一個惡劣網(wǎng)絡環(huán)境的參數(shù),上文中提到的帶寬、延時和丟包率的模擬當然也可以組合起來使用,同時在設置pipe規(guī)則時還可以進一步設置更加精細的規(guī)則,比如指定的ip段,指定的協(xié)議等,比如這一段指令:
ipfw add pipe 4 src-ip 10.1.2.0/24 in
ipfw pipe 4 config bw 1Mbit/s delay 123 plr 0.1
就設置了一個只會介入源ip來自10.1.2.0/24網(wǎng)段的數(shù)據(jù)包的pipe,并且這個pipe會限制1Mbit/s的帶寬,延時123ms,并且有10%的丟包率,通過組合這些參數(shù),可以模擬出很復雜的惡劣網(wǎng)絡環(huán)境,從而滿足開發(fā)和測試的需要。相關的設置參數(shù)的說明和設置方法可以參考官網(wǎng)的相關文檔和其他的一些說明文檔。
在最后,當完成模擬惡劣網(wǎng)絡環(huán)境后,執(zhí)行以下命令:
ipfw -q flush
ipfw -q pipe flush
來清除所有設定的pipe和規(guī)則,進一步如果需要還原原來的環(huán)境的話可以將驅動刪除,并重啟計算機即可。
三、clumsy 0.2模擬網(wǎng)速
clumsy 能在 Windows 平臺下人工造成不穩(wěn)定的網(wǎng)絡狀況,方便你調(diào)試應用程序在極端網(wǎng)絡狀況下的表現(xiàn)。
簡介
利用封裝 Winodws Filtering Platform 的WinDivert 庫, clumsy 能實時的將系統(tǒng)接收和發(fā)出的網(wǎng)絡數(shù)據(jù)包攔截下來,人工的造成延遲,掉包和篡改操作后再進行發(fā)送。無論你是要重現(xiàn)網(wǎng)絡異常造成的程序錯誤,還是評估你的應用程序在不良網(wǎng)絡狀況下的表現(xiàn),clumsy 都能讓你在不需要額外添加代碼的情況下,在系統(tǒng)層次幫你達到想要的效果:
特色:
- 下載即用,不需要安裝任何東西。
- 不需要額外設置,不需要修改你的程序的代碼。
- 系統(tǒng)級別的網(wǎng)絡控制,可以適用于命令行,圖形界面等任何 Windows 應用程序。
- 不僅僅只支持 HTTP,任何 TCP, UDP 的網(wǎng)絡連接都可以被處理。
- 支持本地調(diào)試(服務器和客戶端都在 localhost)
- "熱插拔",你的程序可以一直運行,而 clumsy 可以隨時開啟和關閉。
- 實時調(diào)節(jié)各種參數(shù),詳細控制網(wǎng)絡情況。
實例
下面的動畫展示了 clumsy 作用于一個本地的基于 netcat 的 UDP 服務器/客戶端的情況。仔細觀察你可以看到數(shù)據(jù)根據(jù)在 clumsy 的影響下產(chǎn)生了相應的變化。 如果你基本知道了 clumsy 是干什么用的,不妨到下載頁面選擇適用于你系統(tǒng)的版本進行下載。
詳細信息
clumsy 首先根據(jù)用戶選擇的 filter 來攔截指定的網(wǎng)絡數(shù)據(jù)。在 filter 中可以設定你感興趣的協(xié)議(tcp/udp),端口號,是接收還是發(fā)出的端口。你也可以通過簡單的邏輯語句來進一步縮小范圍。當 clumsy 被激活時,只有符合這些標準的網(wǎng)絡數(shù)據(jù)會被進行處理,而你不感興趣的數(shù)據(jù)仍然會由系統(tǒng)正常傳輸。
當被 filter 的網(wǎng)絡數(shù)據(jù)包被攔截后,你可以選擇 clumsy 提供的功能來有目的性的調(diào)整網(wǎng)絡情況:
- 延遲(Lag),把數(shù)據(jù)包緩存一段時間后再發(fā)出,這樣能夠模擬網(wǎng)絡延遲的狀況。
- 掉包(Drop),隨機丟棄一些數(shù)據(jù)。
- 節(jié)流(Throttle),把一小段時間內(nèi)的數(shù)據(jù)攔截下來后再在之后的同一時間一同發(fā)出去。
- 重發(fā)(Duplicate),隨機復制一些數(shù)據(jù)并與其本身一同發(fā)送。
- 亂序(Out of order),打亂數(shù)據(jù)包發(fā)送的順序。
- 篡改(Tamper),隨機修改小部分的包裹內(nèi)容。
盡管當前寬帶網(wǎng)絡連接十分普及,但網(wǎng)絡傳輸其本身在本質上總不是穩(wěn)定的。如果你的應用程序中沒有應對各種情況的處理,那么有可能一個丟失的 UDP 包裹都會讓你的程序崩潰。正確的調(diào)試這類行為 顯然需要再代碼結構上進行仔細的設計和處理,還會很花功夫。而且在某些封裝緊密的開發(fā)環(huán)境(Unity3D 自帶的網(wǎng)絡庫可能是一個例子)下會更麻煩。clumsy 以盡可能減輕程序員負擔為目標, 希望提供一個簡單方便(但并不完美)的解決方案。
項目的代碼可以在github上獲取。在下載頁面有編譯好的版本。強烈建議在使用前花點時間閱讀一下文檔,來 了解 clumsy 的功能和限制。