对于IPerf源码解析,我是基于2.0.5版本在Windows下执行的情况进行分析的,提倡开始先通过对源码的简单修改使其能够在本地编译器运行起来,这样可以打印输出一些中间信息,对于理解源码的逻辑,程序实现的过程能够起到事半功倍的效果。

IPerf主要分为如下几个模块:

  • 选项参数处理;
  • 线程封装和角色扮演;
    • 四种线程模式(或者说角色):

      • 客户端线程;
      • 服务端线程;
      • 报告者线程;
      • 监听者线程。
  • 套接字选项设置与提取;
  • 链表和数组的封装和维护;
  • 处理多并发Condition条件变量的封装;
  • 时间戳封装;
  • Windows下作为后台服务运行的创建和运行。

下面尽可能针对每个模块进行说明:

选项参数的处理:

作为命令行控制台应用程序,首要考虑到的问题就是对输入参数命令行选项的处理,如果是简单的应用程序直接通过case-switch或者if条件语句或许可以解决,但是一旦到了规模较大,实现内容较为复杂的控制台应用程序,比如IPerf,还是用该处理方法就显得相对笨拙,在性能、逻辑处理等方面都有所不及。

对于选项参数的处理,IPerf使用的GUN的一个getopt文件,在Linux下已有该头文件,而在Windows需要自己导入该头文件和实现文件,加入文件之后,还需要做的就是对文件中的一些长选项和短选项字符串进行处理,因为这是自己定义的需求,处理选项参数的逻辑是一定的,但是要将哪些内容作为合理的选项和参数以及操作数,那些又是非法的字符和未能识别的操作数,程序需要据此进行判断,所以需要进行一个初始化的过程,后面在使用的过程中调用相应的接口对主函数传进来的args和argv[]作为输入参数进行处理就行了,更多关于选项参数的处理,可以看看该篇文章,或者自行网上找寻。

程序的主要模块就是角色线程的生成、运行和销毁,其他模块包括时间戳、条件变量、维护的链表等都是为此服务的,所以这里打算先说一下其他模块然后在逐一分析不同类型的线程。

套接字选项的封装和设置:

说套接字选项之前还需要先说一下套接字的生成,IPerf对套接字Socket的生成定义了一个名为WIN32Socket的宏,这个宏内部调用了WSASocket,而套接字的属性和协议类型是通过定义WSAPROTOCOL_INFO类型静态函数,并将该函数作为输入参数传到WASSocket实现的。

PerfSocket.cpp中只有一个名为SetSocketOptions的函数,顾名思义就是用来设置套接字选项的值,函数里面包含设置TCP滑动窗口大小(setsock_tcp_windowsize函数在另一个名为tcp_window_size.c的文件中单独实现)、设置拥塞控制、设置多播、设置IP服务类型(这个很少用得到)、设置最大报文段大小(setsock_tcp_mss函数在sockets.c文件中实现)、设置非延迟等。当然,除了设置套接字选项外,也有获取相应选项的函数,比如getsock_tcp_windowsize和getsock_tcp_mss。

在SocketAddr.c文件中,IPerf定义了一系列以“SocketAddr_函数功能”格式命名的函数,通过宏条件判断是否支持IPV6,定义了包括:通过IP地址获取到或者说转换成对端的套接字地址结构,将网络序转成点分十进制,获取和设置端口值等,围绕着定义的iperf_sockaddr类型(IPV4下为sockaddr_in类型,IPV6下为sockaddr_storage)判断该套接字地址是否相同等。

链表和数组的维护和封装:

IPerf在实现中创建了几种不同类型的链表和数组:在开始时的线程链表,报告使用的报告者首部链表,监听(者)线程维护的客户端链表,紧接在传送类型报告者首部后面的包数组,在服务端和多并发客户端维护的多组报告首部维护的传输信息数组。具体的接下来会详细讲述到,List.cpp封装对Iperf_ListEntry类型链表的增删查和销毁操作,而该链表仅是监听者用来存储和维护已连接客户端的信息,别无它用。

处理多并发Condition条件变量的封装:

Condition是IPerf自己封装的结构体,变量mCondition为事件内核对象的句柄,变量mMutex为互斥量的句柄,

Condition_Initialize( Cond ): 创建一个初始化就处于触发状态的互斥量并把返回的句柄值赋予mMutex,创建一个初始化为未触发状态的手动重置事件并把返回的句柄值赋予mCondition;

Condition_Destroy( Cond ):通过mCondition和mMutex的句柄值销毁事件内核对象和互斥量;

Condition_Lock( Cond )  == Mutex_Lock( &Cond.mMutex ) == WaitForSingleObject( Cond.mMutex, INFINITE )

Condition_Unlock( Cond ) == Mutex_Unlock( &Cond.mMutex ) == ReleaseMutex( Cond.mMutex )

Condition_Wait( Cond ): 首先释放互斥量,接着阻塞永久等待事件发生,然后等待互斥量;

Condition_TimedWait( Cond ): 首先释放互斥量,接着阻塞在一定的时间内等待事件发生,然后等待互斥量;

Condition_Signal( Cond ):因为是手动重置事件,当其被调用时,所有正在等待该事件的线程都会变成可调度状态;首先需要了解SetEvent和PulseEvent的区别,因为是手动重置事件,这对于两个函数就有区别了,在自动重置事件类型下事件发生后被等待接收后会自动重置为未触发状态,具体可以查看《Windows核心编程》第9章的内容,里面还介绍了SignalObjectAndWait函数的作用呢。

Condition条件变量定义的宏在使用过程中自己是不太理解的,因为调用的时候容易将等待事件和获取互斥量相互混淆,明明刚释放了互斥量然后永久等待事件发生时,好不容易等到事件发生了又要获取互斥量的所有权,所以写者在每次等待和每次进入以及随后的退出Condition时都加了相应的Debug输出,这样或许能够容易理解点,因为时间的关系,只能将这事放到后面去啃明白,但是在输出的过程中确实能发现其起到的作用,比如报告者在无可奉告的情况下等待输出内容的产生。

时间戳:

估计为了与UNIX统一起来,IPerf在Win32上不是直接调用API使用时间,而是自己封装了gettimeofday,先通过GetSystemTimeAsFileTime获取的UTC格式的时间转换成UNIX新纪元下的时间并通过timeval类型进行返回,在该实现函数中使用了几个特殊的数字,这在源代码行上的注释已经说明清楚,这里不再讲述;

TimeStamp这个类中就只有一个类型为timeval的成员变量,成员函数包括获取当前的时间,对时间进行相加减,比较两个时间的先后等,比较容易理解。

时间戳主要用在数据传输过程中给每个发送包赋值,表明这个包发送的时间;还有在-i选项在使用的条件下,计算每次需要打印报告的时间,通过比较将要打印报告的时间和最新发送包的时间戳,决定是否打印这段时间发送的带宽和发送的数据量以及发送的时间段。

作为后台服务运行:

仅用于服务端,并且在作为后台服务运行时,-o filename 选项参数才能起到作用,同样也是加入他人实现的文件,稍微看了一下,是通过SCManager的API才创建和执行服务的,这个后面有时间再认真学习,可以考虑自己在后面的某些项目中可以复用。

本文暂且就讲到这里,下一篇开始讲解线程和角色,也就是结合着线程讲解客户端、服务端、报告者、监听者的执行过程,暂且仅在TCP模式下,UDP后续再来说明,当然理解了TCP模式下的运行逻辑后,相信UDP模式下也不难理解。

IPerf——网络测试工具介绍与源码解析(2)的更多相关文章

  1. IPerf——网络测试工具介绍与源码解析(4)

    上篇随笔讲到了TCP模式下的客户端,接下来会讲一下TCP模式普通场景下的服务端,说普通场景则是暂时不考虑双向测试的可能,毕竟了解一项东西还是先从简单的情况下入手会快些. 对于服务端,并不是我们认为的直 ...

  2. IPerf——网络测试工具介绍与源码解析(1)

    IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少 ...

  3. IPerf——网络测试工具介绍与源码解析(3)

    [线程的生成]   生成线程时需要传入一个thread_Settings类型的变量,thread_Settings包含所有线程运行时需要的信息,命令行选项参数解析后所有得到的属性都存储到该类型的变量中 ...

  4. IPerf——网络测试工具介绍与源码解析(5)

    本篇随笔讲述一下TCP协议下,双向测试模式和交易测试模式下客户端和服务端执行的情况: 双向测试模式: 官方文档的解释 Run Iperf in dual testing mode. This will ...

  5. Android IntentService使用介绍以及源码解析

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...

  6. vue系列---Mustache.js模板引擎介绍及源码解析(十)

    mustache.js(3.0.0版本) 是一个javascript前端模板引擎.官方文档(https://github.com/janl/mustache.js) 根据官方介绍:Mustache可以 ...

  7. JUC中Lock和ReentrantLock介绍及源码解析

    Lock框架是jdk1.5新增的,作用和synchronized的作用一样,所以学习的时候可以和synchronized做对比.在这里先和synchronized做一下简单对比,然后分析下Lock接口 ...

  8. 【转载】Android IntentService使用全面介绍及源码解析

    一 IntentService介绍 IntentService定义的三个基本点:是什么?怎么用?如何work? 官方解释如下: //IntentService定义的三个基本点:是什么?怎么用?如何wo ...

  9. Android HandlerThread使用介绍以及源码解析

    摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.HandlerThread的介绍及使用举例              HandlerThread是什么鬼?其本质就是一个线程,但是Han ...

随机推荐

  1. noteless的博客导航页 所有文章的导航页面

    导航  <spring springmvc mybatis maven 项目整合示例系列-导航页> <JAVA 基础知识点拾遗系列 JAVA学习 -1层  导航页> <计 ...

  2. windows上python的安装

    一,python3.X的点击式安装 第一次写博客,我就是想记载一下自己对Python的探索过程,理解过程,学习过程,我接触python已经一年多了,但是真正的学习摸索是半年前,现在才走上正轨,这是我刚 ...

  3. Linux文件权限与属性详解 之 su & sudo

    Linux文件权限与属性详解 之 一般权限 Linux文件权限与属性详解 之 ACL Linux文件权限与属性详解 之 SUID.SGID & SBIT Linux文件权限与属性详解 之 ch ...

  4. 隐藏马尔科夫模型HMM

    概率图模型 HMM 先从一个具体的例子入手,看看我们要解决的实际问题.例子引自wiki.https://en.wikipedia.org/wiki/Hidden_Markov_model Consid ...

  5. sqlserver数据库 IsNull()

    Isnull 函数主要作用是将为空的值替换为指定值,如果不为空返回检查类型的返回值, 语法:Isnull (check_expression , replacement_value) isnull(参 ...

  6. EF to Sqlite

    测试下来,使用到下面的版本: EF6.1 System.Data.SQLite.EF6.1.0.93.0 System.Data.SQLite.Core.1.0.93.0  注意事项: 设置Autoi ...

  7. [PHP]算法-队列结构的PHP实现

    题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 思路: 1.php数组完全就能实现 2.array_push 从尾部往里压入元素 3.array_shi ...

  8. Redis的数据结构

    Redis的数据结构 redis是一种高级的key-value的存储系统,其中value支持五种数据类型. 字符串(String) 哈希(hash) 字符串列表(list) 字符串集合(set) 有序 ...

  9. Centos6.5安装MySQL5.6备忘记录

    Centos6.5安装MySQL5.6 1. 查看系统状态 [root@itzhouq32 tools]# cat /etc/issue CentOS release 6.5 (Final) Kern ...

  10. 多线程(二)ThreadLocal

    ThreadLocal public class Demo extends Thread{ static int i = 0; public Integer getNext(){ i++; retur ...