1. 时延的测试
时延是非常重要的QOS参数,这里和大家分享一下使用iperf,tcpdump和perl脚本一起完成对网络时延的测试。iperf大家都知道是发包工具,采用C/S模式,可以得到网络的带宽,抖动和丢包率,唯独不能得到时延,在该测试环境中,我们采用iperf发送UDP包来获得时延。
tcpdump是抓包工具,把网络中传送的包完全截获下来。Perl在这里则是对抓到的包进行分析,得到时延的平均值。
测试时延最好构造一个回环的测试路径,也就是我们常说的RTT时延。而如何需要测试单向的时延则比较复杂,需要硬件较好的支持,因为必须要进行机器之间的同步。有兴趣的朋友可以参考http://www.internet2.edu/performance/owamp/index.html
2. tcpdump时间戳
时延测试所依赖的时间戳是tcpdump的时间戳,那么这个时间戳是怎样得来的呢?
tcpdump通过从libpcap中得到时间戳,libpcap是独立于系统的用户级别的网络数据包的捕获接口。libpcap从操作系统中得到时间戳的值。
时间戳放置在Tcpdump的文件头的后面,链路层数据的前面。再后面就是我们所熟知的IP,TCP/UDP等。
Tcpdump文件头以一个24字节的文件头开头。前四个字节是tcpdump文件标志“A1 B2 C3 D4”或为“D4 C3 B2 A1”。具体顺序取决于机器的大小端。
时间戳一般占用8个字节,对应为秒和微秒,各4个字节,其数据结构如下:
struct tsp_timeval {
u_int32_t tv_sec;
u_int32_t tv_usec;
};
当然,有些系统将timestamp定义成64bit。此时时间戳就将占用16个字节。
3. iperf UDP结构
这里有必要提示一下iperf的UDP结构,主要考虑到我们在计算时延的时候必须针对的是同一个包,iperf中使用了32bit的id来标识包的序号。
具体看看UDP包的数据部分前面12个字节数据结构定义:
// used to reference the 4 byte ID number we place in UDP datagrams
// use int32_t if possible, otherwise a 32 bit bitfield (e.g. on J90)
typedef struct UDP_datagram {
#ifdef HAVE_INT32_T
int32_t id;
u_int32_t tv_sec;
u_int32_t tv_usec;
#else
signed int id : 32;
unsigned int tv_sec : 32;
unsigned int tv_usec : 32;
#endif
} UDP_datagram;
该结构包括一个32位的id域和用来表明数据包发送时间(timestamp)tv_sec(秒)和tv_usec(微秒)。
其中Id从0开始计数。
服务器端根据id域的值计算丢包率参数,而根据tv_sec和tv_usec计算抖动参数。注意这里的timestamp和tcpdump包头部的timestamp不是一回事。
4. perl脚本计算时延
如果需要的测试用例比较少,则可以通过wireshark图形化工具进行分析。Wireshark是我们熟悉的Ethereal的前身。笔者曾经尝试过这样一种方法,用wireshark打开tcpdump log,export成csv文件,然后利用excel的过滤功能和快速计算能力来获得时延,当需要分析的log较少时,可以采用此法。也比较方便。
但是如果被测用例比较多,纯粹的依靠手工操作的话工作量大,且繁杂容易出错。故考虑通过脚本实现,以降低工作量。
使用perl脚本进行分析也相当简单,主要依赖两个module,tcpdumplog和Netpacket。
Tcpdumplog中有个header的方法,通过它就可以提取tcpdump时间戳。将接收到的时间减去发送的时间就是该包的时延。考虑到存在丢包的可能,此时就需要用到iperf UDP结构中32位的id域了,以保证针对的是同一个包计算时延。