首页 > 代码库 > Iperf软件介绍

Iperf软件介绍

    Iperf 版本建议采用linux版本,事实上,windows版也很好用。Iperf 是一个 TCP/IP 和 UDP/IP 的性能测量工具,通过调谐各种参数可以测试TCP的最大带宽,并报告带宽、延迟,最大段和最大传输单元大小等统计信息。Iperf可以运行于Linux/BSD、Unix及Windows等操作系统。

    带宽测试通常采用UDP模式,因为能测出极限带宽、时延抖动、丢包率。在进行测试时,首先以链路理论带宽作为数据发送速率进行测试,例如,从客户端到服务器之间的链路的理论带宽为100Mbps,先用 -b 100M进行测试,然后根据测试结果(包括实际带宽,时延抖动和丢包率),再以实际带宽作为数据发送速率进行测试,会发现时延抖动和丢包率比第一次好很多,重复测试几次,就能得出稳定的实际带宽。

1、UDP 模式

服务器端    iperf -u -s

客户端   

iperf -u -c 192.168.1.1 -b 100M -t 60

在udp模式下,以100Mbps为数据发送速率,客户端到服务器192.168.1.1上传带宽测试,测试时间为60秒。

iperf -u -c 192.168.1.1 -b 5M -P 30 -t 60

客户端同时向服务器端发起30个连接线程,以5Mbps为数据发送速率。

iperf -u -c 192.168.1.1 -b 100M -d -t 60

以100M为数据发送速率,进行上下行带宽测试。

2、TCP模式

服务器端    iperf -s

客户端

iperf -c 192.168.1.1 -t 60

在tcp模式下,客户端到服务器192.168.1.1上传带宽测试,测试时间为60秒。

iperf -c 192.168.1.1 -P 30 -t 60

客户端同时向服务器端发起30个连接线程。

iperf -c 192.168.1.1 -d -t 60

进行上下行带宽测试。

另外,

-p 监听或者连接的端口号

-w TCP滑动窗口的大小

3、Iperf工作原理

    Iperf主要的功能是测试基于特定路径的TCP连接的性能,我们知道TCP连接调整最基本的措施是调整TCP窗口的大小,窗口大小控制在任何节点网络中可以存在的数据大小。如果该值太小,发送者将会在某段时间处于空闲状态,从而影响发送的性能。TCP窗口大小的理论值为链路瓶颈带宽与往返时延的乘积:

     TCP_Window=Bottleneck_Bandwidth*Round_Trip_Time

例如链路瓶颈带宽为45Mbps,往返时延为42ms(可以通过ping来测试),那么窗口的理论值为:

     45Mbps*42ms=(45*e6)*(42*e-3)=1890000 bits=230KByte

调节窗口大小即可以理论值为基准,在该值上慢慢增大或减少,即可获得最好的结果。

    Iperf测试TCP带宽的原理较简单,即在客户端和服务器端建立连接(三次握手)后,客户端发送一定大小的数据报,并记下发送的时间, 或者客户端在一定的时间内发送数据,并记下发送的总数据。带宽的大小等于发送的总数据除以发送的总时间。对服务器端来说,就是在连接建立时间内,接收的总数据除以所花时间即为服务器端所测得的带宽。MSS的大小通过TCP内核接口函数直接获得。

    Iperf测试UDP的性能时,客户端可以指定UDP数据流的速率。客户端发送数据时,将根据客户提供的速率计算数据报发送之间的时延。另外客户还可以指定发送数据报的大小。每个发送的数据报包含一个ID号,用来惟一地标识该报文。服务器端则根据该ID号来确定数据报丢失和乱序。当把UDP报文大小设置可以将整个报文放入IP层的包(packet)内时,那么UDP所测得的报文丢失数据即为IP层包的丢失数据。这提供了一个有效的测试包丢失情况的方法。数据报传输延迟抖动 (Jitter)的测试由服务器端完成,客户发送的报文数据包含有发送时间戳,服务器端根据该时间信息和接收到报文的时间戳来计算传输延迟抖动。传输延迟抖动反映传输过程中是否平滑。由于它是一个相对值,所以并不需要客户端和服务器端时间同步。

4、Iperf实现

     Iperf源代码采用面向对象的C++语言实现,主要包括基本类实现类两部分。基本类提供了实现中需要用到的一些基本的对象,包括队列、链表、时间管理、锁、条件、线程等,这些代码不是特定于Iperf应用的,可以移植到其他应用程序。实现类中主要包括针对Iperf应用的类,包括实现客户端/服务器端发送和接收数据的类,以及用于统计信息的类等。这里主要讨论一下与应用关系最紧密的几个类,其他的类不做详述(转载者注:后面转载文章有讲解)。

    Iperf主要类图结构包括9个类。Iperf 的核心部分均在PerfSocket类中实现,包括客户端和服务器端发送和接收数据、带宽报告、数据丢失及延迟抖动报告,以及窗口大小和MSS报告等功能。其中Speaker和Client为客户端的对象,Listener、Audience和Server为服务器端的对象。客户端和服务器端的通信通过三个消息完成:connect、write以及shutdown。这里connect不同于TCP中的连接,它还包含一个数据报文,其信息为双向测试而传给服务器端 的信息,主要用于双向测试时让服务器端启动客户端线程而所需要的信息。UDP 测试的过程基本上跟TCP类似。UDP报文包含了一个应用报文头,其主要字段为报文ID和时间信息,这个主要是为了测试UDP报文的丢失、乱序以及延迟抖动性能。UDP的第一个报文用来建立连接,不作为应用数据,其信息为双向测试而传给服务器端的信息,主要用于双向测试时让服务器端启动客户端线程而所需要的信息。UDP与TCP第一个报文内容的主要区别是UDP报文还包括一个应用报文头。UDP传输结束通过客户端发送一个FIN的报文来实现,该报文的报文ID为负数,服务器端接收到FIN报文后即停止接收报文并回送一个 AckFIN报文给客户,AckFIN报文包含了服务器端得到的测试数据。

操作举例:

1)TCP测试

服务器执行:iperf -s -i 1 -w 1M

客户端执行:iperf -c host -i 1 -w 1M

其中-w表示TCP window size,host需替换成服务器地址。

2)UDP测试

服务器执行:iperf -u -s

客户端执行:iperf -u -c 10.32.0.254 -b 900M  -i 1  -w 1M  -t 60

其中-b表示使用带宽数量,千兆链路使用90%容量进行测试就可以了。

5Iperf提供的库

    在开发Iperf的过程中,开发者把 Socket编程和多线程编程中经常用到的一些系统调用封装成对象,屏蔽了底层函数的复杂接口,提供了模块化和面向对象的机制,也为我们提供了一些非常实用的编程工具,我们可以在实现自己的程序时复用这些类。由于这些类实现的源代码都比较简单,也为我们修改前人的代码实现自己的功能提供了方便。

    这些类的定义与实现都在源代码文件夹的lib子文件夹下。主要包括以下一些对象:

    SocketAddr类:封装了Socket接口中的网络地址结构(sockaddr_in等)以及各种地址转换的系统调用(gethostbyname、gethostbyaddr、inet_ntop等);
    Socket类:封装了socket文件描述符,以及socket、listen、connect等系统调用;
    Mutex类以及Condition类:封装了POSIX标准中的mutex和condition(条件变量)线程同步机制;
    Thread类:封装了POSIX标准中的多线程机制,提供了一种简单易用的线程模型;
    Timestamp类:通过Unix系统调用gettimeofday实现了一个时间戳对象,提供了获得当前时间戳,计算两个时间戳之间的先后关系等方法。

    此外,在lib文件夹中还包括一些Iperf的实现提供的实用工具函数,包括endian.c文件中的字节序转换函数、gnu_getopt文件中的命令行参数处理函数、snprintf文件中的字符串格式化函数、signal.c文件中的与信号处理有关的函数、 string.c文件中的字符处理函数、tcp_window_size.c文件中的TCP窗口大小处理函数等。

6、Iperf工作机制

    Iperf是基于Server-Client模式实现的。在测量网络参数时,Iperf区分听者(Audience)和说者(Speaker)两种角色。说者向听者发送一定量的数据,由听者统计并记录带宽、时延抖动等参数。说者的数据全部发送完成后,听者通过向说者回送一个数据包,将测量数据告知说者。这样,在说者和听者两边都可以显示记录的数据。如果网络过于拥塞或者误码率较高,当听者会送的数据包无法被说者接收到时,说者无法显示完整的测量数据,而只报告本地记录的部分网络参数,发送的数据量、发送时间、发送带宽等,像时延抖动等参数在说者一侧则无法获得(因此,在报告时,服务器和客户端所得到的信息是不同的)。
    Iperf提供了三种测量模式:Normal、Tradeoff、Dualtest。对于每一种模式,用户都可以通过-P选项指定同时测量的并行线程数。以下的讨论假设用户的并行线程数为P个。
    在Normal模式下,Client生成P个说者线程,并行向Server发送数据。Server每接收到一个说者的数据,就生成一个听者线程,负责与说者间的通信。Client有P个并行的说者线程,而Server有P个并行的听者线程,两者之间共有P个连接同时收发数据。测试结束后,Server端的每个听者向自己对应的说者回送测得的网络参数。
    在Tradeoff模式下,首先进行Normal模式下的测试过程,然后Server和Client互换角色。Server生成P个说者线程,同时向Client发送数据。Client对应每个说者生成对应的一个听者线程用于接收数据并测量参数。最后,有Client端的听者向Server端的说者回馈测量结果。这样就可以测量两个方向上的网络参数了。
    对于Dualtest模式同样可以测试两个方向上的网络参数,与Tradeoff模式的不同在于,在Dualtest模式下,由Server到Client方向上的网络测试与由Client到Server方向上的网络测试是同时进行的。Client生成P个说者和P个听者线程,说者向Server端发送数据,听者等待接收Server端的说者发送的数据。Server端也进行同样的操作。在Server端和Client端之间同时存在2P个网络连接,其中有P个连接的数据由Client流向Server,另外P个连接的数据由Server流向Client。因此,Dualtest的模式需要的测试时间是Tradeoff模式的一半。
    在三种模式下,除了P个听者或说者进程,在Server和Client两侧均存在一个监控线程(monitor thread)。监控线程的作用包括:
    *生成说者或听者线程;
    *同步所有说者或听者的动作(开始发送、结束发送等);
    *计算并报告所有说者或听者的累计测量数据。
    在监控线程的控制下,所有P个线程间就可以实现同步和信息共享。说者线程或听者线程向一个公共的数据区写入测试结果数据(此数据区位于实现监控线程的对象中),由监控线程读取并处理。通过互斥锁(Mutex)实现对该数据区的同步访问。Server可以同时接收来自不同Client的连接,这些连接是通过Client的IP地址标识的。Server将所有Client的连接信息组织成一个单向链表,每个Client对应链表中的一项,该项包括该Client的地址结构(sockaddr)以及实现与该Client对应的监控线程的对象(我们称之为监控对象),所有与此Client相关的听者对象和说者对象都是由该监控线程生成的。
    Iperf中的主要类:
   
    PerfSocket类:
    PerfSocket类以Socket为基类派生而来。该类实现了Iperf用于通信的大多数功能,包括发送UDP数据包(Send_UDP)、接收UDP数据包(Recv_UDP)、发送TCP数据包(Send_TCP)、接收TCP数据包(Recv_TCP)以及传输初始化、报告网络参数、发送/回复结束包等。
    Notify类:
    Notify类是Iperf中的另一个主要基类。它实现了对多个并行线程的监控机制。Notify类提供了对所监控线程的开始动作、结束动作进行同步的方法。这些方法主要是通过条件变量(Condition)机制来实现的。同时,Notify类的实例还含有供被监控的线程进行排他(exclusively)访问(互斥访问)的数据区。一般是被监控的线程向该数据区中写入数据。由Notify类的实例读取。
    Listener类:
    Listener类以PerfSocket类和Thread类为基类,属多重继承。因为是Thread类的派生类,因此 一个Listener实例就是一个执行线程,又因为Listener类是PerfSocket类的派生类,因此它也具有收发socket数据的能力。 在服务器端,在服务器程序启动后Listener就存在并开始工作。在客户端,如果用户指定进行双向测试(tradeoff或dualtest模式),也 会生成Listener对象。Listener实现的主要功能是在设定的端口上监听连接请求,接收到请求后,若请求来自一个新的客户,Listener生 成 一个Audience(听者监控线程,也可称为听者监控对象,见下文)实例来负责继续处理与该请求对应的后续操作,如果在请求中指明了要进行 tradeoff或dualtest模式的测量,Listener就生成一个Speaker(说者监控线程,也可称为说者监控对象,见下文)实例来负责发 起到客户的反向连接;如果请求来自一个已经存在的客户,Listener通过该客户对应的Audience实例的方法使该Audience实例生成一个听 者线程进行处理该连接(这发生在多线程并行测量的情况下)。完成这些工作后,Listener线程返回继续监听。
    Audience类:
    Audience类以Thread类和Notify为基类,属多重继承。一个Audience实例对应一个执行线程且具有监控其他线程的功能。Audience实例(线程)的主要功能是作为听者线程的监控线程。 它首先生成一个听者线程(听者线程通过Server类实现,见下文),之后负责维护并报告所用听者线程测量结果的累计值,在对听者线程的结束事件同步之 后,Audience线程退出。Listener线程在接收到一 个已有客户的连接请求时,也会通过Audience实例生成新的Server实例。在服务器端,一个客户对应一个Audience实例,所有的 Audience实例组织成一个链表。
    Speaker类:
    Speaker类同Audience类一样,也以Thread类和Notify类为基类。它实现了说者线程的监控线程。 它首先生成若干说者线程(听者线程通过Client类实现,见下文),对所有说者线程的开始事件进行同步,之后负责维护并报告所用说者线程测量结果的累计 值,在对说者线程的结束事件同步之后,Speaker线程退出。此外,在Speaker线程看是运行时,还会根据是否进行双向测试生成Listener生 成实例,具体情况会在下文中介绍。在客户端程序开始运行后,Speaker线程就开始工作了。当服务器端收到双向测试的请求时,也会生成Speaker实 例(线程)。
    Server类:
    Server类以PerfSocket类和Thread类为基类,实现了听者进程。它接收对端的说者对象发来的数据,记录数据量、延迟抖动等网络参数,打印测试结果,修改听者监控进程(Audience实例)的累计数据区,并在收到说者的最后一个数据包时将测试结果回送给说者进程。
    Client类:
    Server类以PerfSocket类和Thread类为基类,实现了听者进程。它接收对端的说者对象发来的数据,记录数据量、延迟抖动等网络参数,打印测试结果,修改听者监控进程(Audience实例)的累计数据区,并在收到说者的最后一个数据包时将测试结果回送给说者进程。
    综上,Client类与Server类对应,分别实现了说者线程和听者线程;Speaker类和Audience类对应,分别实现了说者监控线程和听者监控线程。Listener类是监听线程;所有的Client实例由Speaker实例生成,所有的Server实例是由Listener实例触发Audience实例生成。