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

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

对于服务端,并不是我们认为的直接创建服务端线程,而是先创建一个监听者线程,在本地绑定套接字后进行蹲点监听。

在Listener类中,Run成员函数执行一个do-while循环接收等待来自对端的连接,循环中调用Accept函数,该函数会阻塞,直至接收到对端的连接并通过thread_Settings*类型的指针参数返回客户端的信息,此时该thread_Settings*类型的值已经可以看作是一个用来生成服务端线程的服务端配置信息,接着创建一个IPerf_ListEntry*类型的节点,此前已经定义一个该类型的全局变量clients链表,作为存储已连接的客户端的信息,对新创建的IPerf_ListEntry*类型的变量listtemp,用新连接产生的套接字地址进行初始化,然后在clients链表中查找是否已存在同一对端连接过来的客户端信息节点,如果有,则与已存在的客户端节点共用MultiHeader*类型的多播报首部,该结构是用来打印并发统计信息的,对应在IPerf——网络测试工具介绍与源码解析(3)中讲述报告者线程时提到的多播类型的报告内容;如果在clients中没找到则说明该连接是一个新的连接,这时需要通过调用InitMulti函数给其创建并分配一个新的MultiHeader结构。

在InitMulti函数中真正执行分配MultiHeader空间的条件有两个:

if ( agent->mThreads > 1 || agent->mThreadMode == kMode_Server ) 

第一个条件对应进行并发测试的客户端,也就是在选项参数中使用了-c和-P,参数选项输入时,-P后面跟着的值说明有几个客户端同时尝试连接并发送数据到服务端;第二个条件对应着传送进来的thread_Settings*类型参数所代表的线程模式是服务端线程模式,因为服务端得考虑到同一个IP地址的客户端发起并发连接或者说通过不同的端口开启多个客户端程序尝试连接到同一个服务端的情况。对于同一个地方来的客户端都得进行一个汇总报告打印。

创建的MultiHeader结构有点类似于ReportHeader,具体如下:

不仅结构类似,在对MultiReort结构进行初始化也跟ReportHeader类似。

对于每一个从客户端连接过来的套接字,监听者线程都会将其封装成IPerf_ListEntry类型并把它添加到clients链表中,紧接着监听者线程会使用该热乎乎的套接字尝试去接收大小为client_hdr类型大小的数据,这是与客户端定的规则,客户端连接上服务端后要发送客户端首部信息到服务端,以便服务端知道客户端接下来要做哪种形式的测试后准备必要的条件,比如说服务端从客户端首部信息解析出此次要进行双向测试,那么监听者线程还得生成一个线程作为客户端连接回去并接下来开始履行客户端线程的职责发送数据回去。

还是继续说监听者线程返回的对端的套接字吧,接着监听者线程会将存储此套接字信息的thread_Settings*类型的变量作为参数生成一个服务端线程进行后续数据的接收,监听者继续监听新的客户端的连接,然后继续生成一个服务端线程,然后再继续监听,生成的若干服务端线程则努力地在一边同时进行着数据的接收,别忘了还有报告者线程一直存在着,报告者线程从一开始就报告着各种信息,开始时是设置类型的信息,接着是连接类型的信息,后面就为服务端线程打印一大堆的传输类型信息,如果服务端线程中有同一客户端连接过来的情况出现,那么报告者线程还得打印多播类型的信息,这就得使用到MultiReport且其在控制台表现的关键字为"[SUM]......"

截图中红色画框标记出来的内容即为多播类型的统计信息,在客户端连接时添加 -P 选项再加上大于1的选项值就会在客户端和服务端出现这种情况。

而上面这张截图中,红色画框的数据就有点诡异了,1.0-5.0秒算出来的传输的数据量为0,带宽也为0bits/sec,原因在哪?其实上面稍微提到了点,就是从同一客户端发出的连接,在服务端被监听线程接收到后,会让其使用同一个MultiHeader,也就是IPerf_ListEntry结构中holder所指向的内容为同一个。

1             if ( exist != NULL )
2             {
3                 // Copy group ID
4                 //将新连接的服务监控线程的多播对象设置为以往的多播对象
5                 listtemp->holder = exist->holder;
6                 server->multihdr = exist->holder;
7             } 

还有一段需要注意的代码:

reporter.c/initReport

 1             if ( reporthdr->multireport != NULL && isMultipleReport( agent ))
 2             {
 3                 //
 4                 reporthdr->multireport->threads++;
 5
 6                 if ( reporthdr->multireport->report->startTime.tv_sec == 0 )
 7                 {
 8                     gettimeofday( &(reporthdr->multireport->report->startTime), NULL );
 9                 }
10                 reporthdr->report.startTime = reporthdr->multireport->report->startTime;
11             }
12             else
13             {
15                 // set start time
16                 gettimeofday( &(reporthdr->report.startTime), NULL );
17             }

以往的时候我们都是直接走else这一步,因为multireport为NULL,isMultipleReport是默认为真的,现在multireport被分配了空间,符合if中的条件则进入if块内执行,当multireport是刚初始化,而不是从别的IPerf_ListEntry中共用而来的时候,startTime.tv_sec的值应该为0的,这时赋值为当前的时间戳;当multireport是从别处共用过来的,那么starttime相对于当前的服务端线程来说(不仅仅是客户端,Server类中的Run函数也调用了InitReport函数)相当于是一个过去许久的时间戳,究竟过去多久,如果客户端是同时并发连接到服务端的话,那么这个时间间隔很短,短到可以忽略,但如果情况是这样的:同一个IP地址的主机,先开一个客户端连接到服务端,在测试还未结束的时候再开一个客户端连接到相同的服务端,那么这个时间戳相对来说就过去得有点久了。 上篇随笔说过starttime和nexttime的作用,nexttime是由starttime和intervalTime计算出来的,而nexttime决定了何时打印传输类型的报告,上篇随笔有段代码:

    else while ((stats->intervalTime.tv_sec != 0 || stats->intervalTime.tv_usec != 0) &&
                  TimeDifference( stats->nextTime, stats->packetTime ) < 0 ) 

假使我有一个新数据包发出去了,那么我就得到了stats->packetTime的值,其实近乎于当前的时间戳,那么对于nexttime这个“年代已久”的时间来说(因为starttime是“年代已久”的时间),我就得在while里循环执行好几次了,但是从stats->nextTime 到 stats->packetTime时间段里只发了一个数据包或者才仅仅几个包的数据(如果stats->packetTime不是从第一个包中获取的时间的话),那么就会看到如上面最近那张从控制台截取的图片所展示的内容一样,打印出一些0数据量0带宽的数据出来,直至退出while循环到下一次进入while循环才会“恢复正常”。

好像扯远了,本来是要解决“[SUM]..."这种多播类型的报告数据是如何打印出来的,但上面的问题迟早要说明的,所以遇到就提前说了,现在回到正题。

report.c/reporter_condprintstats

        reporter_print( stats, TRANSFER_REPORT, force /*Obivously it‘s value is zero which means not printMSS*/);
        if ( isMultipleReport(stats) )
        {
            reporter_handle_multiple_reports( multireport, &stats->info, force );
        }

上面代码,在打印完传输信息后,有一个判断,然后进入reporter_handle_multiple_reports函数,这个函数正常情况下都会进入,因为条件判断中的isMultipleReport默认为真的(通过控制台选项参数可以让其为假),主要是进入这个函数后,总会遇到multireport为NULL或者线程数小于等于1,继而不执行主体的内容,研究下reporter_handle_multiple_reports函数主体的代码,可以很容易理解[SUM]的数据是怎样打印出来的。需要注意几点,同一个IP地址出来的客户端在服务端所对应的服务端线程数,程序有进行记录,如果在某个时间段未统计到相应数目的运输记录信息,也就是属于同一IP地址的服务端线程没有在这个函数跑过,或者跑进不同的时间打印间隔,未达到current->free == reporthdr->threads这个条件,或者线程数只有1,那么就不进行该间隔时间段的统计信息的打印。

时间: 2024-12-22 19:31:56

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

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

对于IPerf源码解析,我是基于2.0.5版本在Windows下执行的情况进行分析的,提倡开始先通过对源码的简单修改使其能够在本地编译器运行起来,这样可以打印输出一些中间信息,对于理解源码的逻辑,程序实现的过程能够起到事半功倍的效果. IPerf主要分为如下几个模块: 选项参数处理: 线程封装和角色扮演: 四种线程模式(或者说角色): 客户端线程: 服务端线程: 报告者线程: 监听者线程. 套接字选项设置与提取: 链表和数组的封装和维护: 处理多并发Condition条件变量的封装: 时间戳封装

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

IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少可以玩上一段时间. IPerf开始出现的时候是在03年,版本是1.7.0,在网上找到的仅有的系列源码解析篇 http://blog.chinaunix.net/uid/11568125/cid-131106-abstract-1.html 就是基于1.7.0 进行介绍和解析的,貌似1.7.0还是使用

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

[线程的生成] 生成线程时需要传入一个thread_Settings类型的变量,thread_Settings包含所有线程运行时需要的信息,命令行选项参数解析后所有得到的属性都存储到该类型的变量中,作为线程生成的传入值能够决定当前线程扮演的角色. thread_Settings结构中有两个thread_Settings*类型的变量runNow和runNext,runNow不为NULL时表示生成当前Setings所决定的线程之前要先生成包含该指针指向的Settings特征信息的线程,换句话说就要并

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

一 IntentService介绍 IntentService定义的三个基本点:是什么?怎么用?如何work? 官方解释如下: //IntentService定义的三个基本点:是什么?怎么用?如何work?*/ 1.IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. 2.Clients send requests through

Android IntentService使用全面介绍及源码解析

一 IntentService介绍 IntentService定义的三个基本点:是什么?怎么用?如何work? 官方解释如下: //IntentService定义的三个基本点:是什么?怎么用?如何work?*/ 1.IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. 2.Clients send requests through

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

Lock框架是jdk1.5新增的,作用和synchronized的作用一样,所以学习的时候可以和synchronized做对比.在这里先和synchronized做一下简单对比,然后分析下Lock接口以及ReentrantLock的源码和说明.具体的其他的Lock实现的分析在后面会慢慢介绍. Lock框架和synchronized 有关synchronized的作用和用法不在具体说明,应该都很熟悉了.而Lock有着和synchronized一样的语意,但是比synchronized多了一些功能,

airbnb 开源reAir 工具 用法及源码解析(一)

reAir 有批量复制与增量复制功能 今天我们先来看看批量复制功能 批量复制使用方式: cd reair ./gradlew shadowjar -p main -x test # 如果是本地table-list 一样要加file:/// ; 如果直接写 --table-list ~/reair/local_table_list ,此文件必须在hdfs上! hadoop jar main/build/libs/airbnb-reair-main-1.0.0-all.jar com.airbnb.

IPerf网络测试工具

IPerf——网络测试工具介绍与源码解析(1) IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少可以玩上一段时间. IPerf开始出现的时候是在03年,版本是1.7.0,在网上找到的仅有的系列源码解析篇 http://blog.chinaunix.net/uid/11568125/cid-131106-abstract-1.html 就是基于1.

Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码:最后再通过实例来学会使用LinkedList.内容包括:第1部分 LinkedList介绍第2部分 LinkedList数据结构第3部分 LinkedList源码解析(基于JDK1.6.0_45)第4部分 LinkedList遍历方式第5部分 LinkedL