本文主要介紹如何測試網絡性能,文章來自博客園RTC.Blacker,歡迎關注微信公眾號blacker,更多詳見www.rtc.help
網絡性能直接決定了視頻通話效果,比如qq,很多時候我們我們覺得通話效果不錯,但有些時候體驗很差,這時候我們怎么判斷是網絡不好還是產品本身質量問題呢?最好的辦法就是有工具能直接測試當前網絡質量,這也正是本文的主題,原文最早來自環信音視頻專家符寧,由kelly進行整理和編輯。
一、性能指標:
測試網絡性能之前我們得先知道衡量網絡性能好壞得幾個指標:
1、帶寬(吞吐量):
1.1、單位時間內傳輸的數據量,單位通常是每秒比特數,記作bps;
1.2、帶寬反映了網絡的傳輸能力,越大越好;
2、丟包
2.1、數據包丟失個數,等於“發送數據包數” - “接受數據包數”;
2.2、丟包反映了網絡可靠性,越小越好;
3、時延
3.1、數據包從發送開始到接收到該數據所耗費的時間,單位通常是毫秒;
3.2、時延反映了網絡的速度,越小越好;
4、抖動
4.1、指時延的變化,即兩個數據包時延的差值;
4.2、抖動反映了網絡的穩定性,越小越好;
5、亂序
5.1、指接收到的數據包順序和發送順序不一致的次數;
5.2、亂序反映了網絡的穩定性,越小越好;
5.3、當亂序比較嚴重時,丟包也會比較嚴重,所以一般都以丟包指標為主,忽略亂序指標;
二、測試方法
方法一:ping
ping是最常見的,幾乎在所有的OS上都有它的存在。 其工作原理如圖:
- Local發送的數據包,Remote收到數據包后原樣發回來;
- 數據包里包含有序號和時間戳信息;
- 序號用於判斷是否丟包;
- 時間戳用於計算來回時延(圖中藍色部分),它等於接收時間減去數據包時間戳;
不同OS的ping命令選項可能會略有差別,以Mac OSX的ping為例:
1 $ping -s 1024 192.168.1.100
2
3 PING www.microsoft.com (23.42.217.205): 1024 data bytes
4
5 1032 bytes from 23.42.217.205: icmp_seq=0 ttl=49 time=83.883 ms
6
7 1032 bytes from 23.42.217.205: icmp_seq=1 ttl=49 time=77.958 ms
8
9 1032 bytes from 23.42.217.205: icmp_seq=2 ttl=49 time=80.053 ms
10
11 1032 bytes from 23.42.217.205: icmp_seq=3 ttl=49 time=78.244 ms
12
13 1032 bytes from 23.42.217.205: icmp_seq=4 ttl=49 time=77.937 ms
14
15 ...
16
17 --- 192.168.1.100 ping statistics ---
18
19 30 packets transmitted, 29 packets received, 3.3% packet loss
20
21 round-trip min/avg/max/stddev = 77.843/95.375/141.314/19.167 ms
其中 -s 1024 指示包的大小為1024字節;從ping結果可以看出,發送了30個包,收到29個包,3.3%的丟包率,最小時延77.843毫秒,最大時延141.314毫秒,平均時延95.375毫秒,時延的標准差19.167。另外,ping用的是ICMP協議,網絡對ICMP協議處理性能,可能跟UDP或TCP是不一樣的,所以測試結果只能做為參考。
小結:ping的優點是簡單便捷,可以測試時延和丟包,缺點是無法測試帶寬。
方法二:iperf
iperf功能功能強一些,可以測帶寬,丟包,抖動, 但是測不了時延。它的工作原理如圖:
從圖中可以看出iperf是單向發數據包,並不會像ping一樣接收方把數據包發回給發送方,所以它是測不了時延,但能測試抖動。
抖動等於接收時間間隔(綠色長度)減去發送時間間隔(藍色長度,即timestamp2-timestamp1)。
下面是一個例子。
服務端:
1 $iperf -u -s -p 12345 -i 1 -w 1000000
2 ------------------------------------------------------------
3
4 Server listening on UDP port 12345
5
6 Receiving 1470 byte datagrams
7
8 UDP buffer size: 977 KByte
9
10 ------------------------------------------------------------
客戶端:
1 $ iperf -u -c 127.0.0.1 -p 12345 -i 1 -t 5 -b 16K -l 62
2
3 ------------------------------------------------------------
4
5 Client connecting to 127.0.0.1, UDP port 12345
6
7 Sending 62 byte datagrams
8
9 UDP buffer size: 9.00 KByte (default)
10
11 ------------------------------------------------------------
12
13 [ 4] local 127.0.0.1 port 59805 connected with 127.0.0.1 port 12345
14
15 [ ID] Interval Transfer Bandwidth
16
17 [ 4] 0.0- 1.0 sec 2.00 KBytes 16.4 Kbits/sec
18
19 [ 4] 1.0- 2.0 sec 1.94 KBytes 15.9 Kbits/sec
20
21 [ 4] 2.0- 3.0 sec 1.94 KBytes 15.9 Kbits/sec
22
23 [ 4] 3.0- 4.0 sec 1.94 KBytes 15.9 Kbits/sec
24
25 [ 4] 4.0- 5.0 sec 2.00 KBytes 16.4 Kbits/sec
26
27 [ 4] 0.0- 5.1 sec 9.87 KBytes 16.0 Kbits/sec
28
29 [ 4] Sent 163 datagrams
30
31 [ 4] Server Report:
32
33 [ 4] 0.0- 5.1 sec 9.87 KBytes 16.0 Kbits/sec 0.046 ms 0/ 163 (0%)
其中 -b 16K 指定了帶寬參數。測試結果為丟包0個,平均抖動為0.046毫秒。
方式三:自己開發
1 or(unsigned second = 0; second < test_seconds; second++)
2
3 {
4
5 for(unsigned ui = 0; ui < 8; ui++)
6
7 {
8
9 sendto 1024 bytes;
10
11 }
12
13 msleep(1000);
14
15 }
從上面可以看出,ping和iperf各有優缺點,通常需要兩者組合才能滿足我們的需求。
有時候現有工具不能滿足實際應用的需求,比如說完全模擬實際業務環境或者在產品里集成測試功能,這時候就需要自己造輪子。
我們這里只討論造輪子得關鍵點之一:如何勻速發送數據?
我們以設定發送包長為1024字節,帶寬為64kbps為例子,討論發送數據的實現方案。
發送數據最簡單的方法就是,起一個線程,每秒直接發送完當前秒的數據,然后sleep一秒,再繼續發送,如下:
這種方法比較簡單,但是因為發送數據是需要花費時間的,假如發送64Kbit花費了5毫秒,實際發送碼率(帶寬)為64/1005≈63.68Kbps,比設定值低一些。
把發送時間考慮在內,第2個改進后的代碼版本如下:
1 for(unsigned second = 0; second < test_seconds; second++)
2
3 {
4
5 unsigned ts_start = gettimestamp();
6
7 for(unsigned ui = 0; ui < 8; ui++)
8
9 {
10
11 sendto 1024 bytes;
12
13 }
14
15 unsigned elapsed = gettimestamp() - ts_start;
16
17 msleep(1000-elapsed);
18
19 }
從大尺度上看,這個版本確實會按設定帶寬發送數據,但從小的的時間片上看,其瞬時發送速率是非常高的。
假如發送64Kbit花費了5毫秒,則瞬時速率為 64*1000/5=12800Kbps,是設定值的20倍。
這種瞬時高發送速率可能會導致網絡中某些路由器或交換機來不及處理而大量丟包,所以我們繼續改進:
“在每發送一個包時check是否發送太快,如果發送太快的話就sleep一下緩一緩”。
改進后的第三個版本如下:
1 uint64_t sent_bytes = 0;
2
3 unsigned kick_time = gettimestamp();
4
5 for(unsigned second = 0; second < test_seconds; second++)
6 {
7 sendto 1024 bytes;
8 sent_bytes += 1024;
9 unsigned elapsed = gettimestamp() - kick_time;
10 unsigned normal = sent_bytes * 1000 * 8 / (64*1000);
11
12 if(normal > elapsed)
13 {
14 msleep(normal-elapsed);
15 }
16 }
這個版本基本能夠按照設定值勻速發送數據了。
當然,它還不是最完美的,當設定帶寬很高而包長很小時,會導致太多的check,占用太多CPU。這里就不繼續改進了,有興趣的看官可以自己實現之。
原文地址:http://blog.easemob.com/?p=308