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 > || 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所指向的内容为同一个。
if ( exist != NULL )
{
// Copy group ID
//将新连接的服务监控线程的多播对象设置为以往的多播对象
listtemp->holder = exist->holder;
server->multihdr = exist->holder;
}
还有一段需要注意的代码:
reporter.c/initReport
if ( reporthdr->multireport != NULL && isMultipleReport( agent ))
{
//
reporthdr->multireport->threads++; if ( reporthdr->multireport->report->startTime.tv_sec == )
{
gettimeofday( &(reporthdr->multireport->report->startTime), NULL );
}
reporthdr->report.startTime = reporthdr->multireport->report->startTime;
}
else
{
// set start time
gettimeofday( &(reporthdr->report.startTime), NULL );
}
以往的时候我们都是直接走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 != || stats->intervalTime.tv_usec != ) &&
TimeDifference( stats->nextTime, stats->packetTime ) < )
假使我有一个新数据包发出去了,那么我就得到了stats->packetTime的值,其实近乎于当前的时间戳,那么对于nexttime这个“年代已久”的时间来说(因为starttime是“年代已久”的时间),因为摆脱不了TimeDifference(...) < 0为真的困境,我就得在while里循环执行好几次,如果从stats->nextTime 到 stats->packetTime时间段里只发了一个数据包或者才仅仅几个包的数据(如果stats->packetTime不是从第一个包中获取的时间的话),那么就会看到如上面最近那张从控制台截取的图片所展示的内容一样,打印出一些0数据量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,那么就不进行该间隔时间段的统计信息的打印。
IPerf——网络测试工具介绍与源码解析(4)的更多相关文章
- IPerf——网络测试工具介绍与源码解析(2)
对于IPerf源码解析,我是基于2.0.5版本在Windows下执行的情况进行分析的,提倡开始先通过对源码的简单修改使其能够在本地编译器运行起来,这样可以打印输出一些中间信息,对于理解源码的逻辑,程序 ...
- IPerf——网络测试工具介绍与源码解析(1)
IPerf是一个开源的测试网络宽带并能统计并报告延迟抖动.数据包丢失率信息的控制台命令程序,通过参数选项可以方便地看出,通过设置不同的选项值对网络带宽的影响,对于学习网络编程还是有一定的借鉴意义,至少 ...
- IPerf——网络测试工具介绍与源码解析(3)
[线程的生成] 生成线程时需要传入一个thread_Settings类型的变量,thread_Settings包含所有线程运行时需要的信息,命令行选项参数解析后所有得到的属性都存储到该类型的变量中 ...
- IPerf——网络测试工具介绍与源码解析(5)
本篇随笔讲述一下TCP协议下,双向测试模式和交易测试模式下客户端和服务端执行的情况: 双向测试模式: 官方文档的解释 Run Iperf in dual testing mode. This will ...
- Android IntentService使用介绍以及源码解析
版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.IntentService概述及使用举例 IntentService内部实现机制用到了HandlerThread,如果对HandlerThrea ...
- vue系列---Mustache.js模板引擎介绍及源码解析(十)
mustache.js(3.0.0版本) 是一个javascript前端模板引擎.官方文档(https://github.com/janl/mustache.js) 根据官方介绍:Mustache可以 ...
- JUC中Lock和ReentrantLock介绍及源码解析
Lock框架是jdk1.5新增的,作用和synchronized的作用一样,所以学习的时候可以和synchronized做对比.在这里先和synchronized做一下简单对比,然后分析下Lock接口 ...
- 【转载】Android IntentService使用全面介绍及源码解析
一 IntentService介绍 IntentService定义的三个基本点:是什么?怎么用?如何work? 官方解释如下: //IntentService定义的三个基本点:是什么?怎么用?如何wo ...
- Android HandlerThread使用介绍以及源码解析
摘要: 版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.HandlerThread的介绍及使用举例 HandlerThread是什么鬼?其本质就是一个线程,但是Han ...
随机推荐
- shiro源码篇 - shiro认证与授权,你值得拥有
前言 开心一刻 我和儿子有个共同的心愿,出国旅游.昨天儿子考试得了全班第一,我跟媳妇合计着带他出国见见世面,吃晚饭的时候,一家人开始了讨论这个.我:“儿子,你的心愿是什么?”,儿子:“吃汉堡包”,我: ...
- 基于vue技术的企业移动办公系统的设计与实现
如何打包: http://www.cnblogs.com/smilehuanxiao/p/7693858.html http://www.cnblogs.com/1314y/p/6207153.htm ...
- 2.ES6引进的新特性——类Class
为什么? ES6中引入了类,类在java/c++等面向对象的编程语言常见,JS引入类是为了在日后使用js开发大型的应用程序,类本质是语法糖(语法上更加人性化) 以前写一个类 function User ...
- 【ASP.NET MVC系列】浅谈ASP.NET MVC 视图与控制器传递数据
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
- IIS中注册.net4.0
1.开始-运行: 2.运行框中输入 cmd ; 3.输入命令 %windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis.exe -i 4.回车 ...
- mybatis_14二级缓存
原理: 同一级缓存原理相似,在sqlsession3不执行增删改的情况下,sqlsession2的查询结果会直接调用sqlsession1的查询结果,具体细节如下: 使用: 开启二级缓存总开关 U ...
- mybatis_09关联查询_一对一
复杂查询时,单表对应的po类已不能满足输出结果集的映射. 所以有些时候就需要关联查询_一对一:通过条件查询结果每个字段都唯一 一对一:模型里面有模型 一对多:模型里面有集合 多对多:集合里面有集合 方 ...
- 【Java每日一题】20170323
20170322问题解析请点击今日问题下方的“[Java每日一题]20170323”查看(问题解析在公众号首发,公众号ID:weknow619) package Mar2017; public cla ...
- java回调函数学习
前不久学习了代理模式,其中有一个核心之一是Proxy.newProxyInstance();这里有三个参数, loader:目标对象的类加载器 interfaces:目标对象实现的所有接口组成的数组 ...
- 应用分类&练手项目计划
应用分类 练手项目 [应用] 通讯录 xx管理 聊天室 [组件] web容器 db 中间件