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 ...
随机推荐
- spring-session用mysql实现session共享实践
前段时间,写了篇<spring-session-data-redis解决session共享的问题>文章,介绍了spring-session使用redis存储实现session共享的内部机制 ...
- 【Vue.js】基于vue的实时搜索,在结果中高亮显示关键词
一.搜素效果如下: 二.核心 1)利用oninput属性来触发搜素功能 2)利用RegExp来对字符串来全局匹配关键字,利用replace方法来对匹配的关键字进行嵌入高亮的<span class ...
- Perl子程序引用和匿名子程序
子程序也有引用,也有匿名子程序.假设你已经具备了数组.hash的引用知识,所以这里简单介绍一下. $ref_sub = \&mysub; # 子程序引用,&符号必须不能少 &{ ...
- Django 系列博客(七)
Django 系列博客(七) 前言 本篇博客介绍 Django 中的视图层中的相关参数,HttpRequest 对象.HttpResponse 对象.JsonResponse,以及视图层的两种响应方式 ...
- Javascript Cookie小插件
var ManagerCookie = function(){ //添加cookie function addCookie(key,value,time,path){ key = encodeURI( ...
- 【转载】MySql新建账号并分配权限
在服务器的MySQL运维过程中,我们有时会遇到新建MySql账号的情况,要求新建的账号只允许某些特定的数据库操作权限,只能访问到特定的数据库,实现数据库权限的严格管理,以下以MySQL-Front管理 ...
- 行为型---中介者模式(Mediator Pattern)
中介者模式的定义 中介者模式,定义了一个中介对象来封装一系列对象之间的交互关系.中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为. 中介者模式的结构 ...
- ADO.NET基础学习 一(连接数据库)
(记录下方便自己复习) 概念 简单地理解为用来连接数据库的类. 工作流程 ①Connection对象用来连接数据库. 两种连接方式:Windows身份验证 / sqlserver验证 private ...
- C#基础知识回顾:1.由WeakReference想到对象的创建与销毁
.Net Framework中,把资源分为托管资源和非托管资源两大类, 托管资源指可以通过.Net Frame垃圾回收器进行回收的资源,主要是指分配在托管堆上你的内存资源,这类资源的回收是不需要人工干 ...
- 从零开始学安全(二十五)●用nmap做端口扫描
以上是常用的端口扫描 -T 用法 每个级别发包时间 当没有使用T 时默认的使用T3级别发包 半开扫描 先探测主机是否存活 再用-sS 扫描端口 容易造成syn 包攻击 就是利用僵尸主机 进 ...