IPerf——網絡測試工具介紹與源碼解析(4)

上篇隨筆講到了TCP模式下的客戶端,接下來會講一下TCP模式普通場景下的服務端,說普通場景則是暫時不考慮雙向測試的可能,畢竟瞭解一項東西仍是先從簡單的狀況下入手會快些。html

 

 

對於服務端,並非咱們認爲的直接建立服務端線程,而是先建立一個監聽者線程,在本地綁定套接字後進行蹲點監聽。網絡

在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,具體以下:post

 

 不只結構相似,在對MultiReort結構進行初始化也跟ReportHeader相似。測試

對於每個從客戶端鏈接過來的套接字,監聽者線程都會將其封裝成IPerf_ListEntry類型並把它添加到clients鏈表中,緊接着監聽者線程會使用該熱乎乎的套接字嘗試去接收大小爲client_hdr類型大小的數據,這是與客戶端定的規則,客戶端鏈接上服務端後要發送客戶端首部信息到服務端,以便服務端知道客戶端接下來要作哪一種形式的測試後準備必要的條件,好比說服務端從客戶端首部信息解析出這次要進行雙向測試,那麼監聽者線程還得生成一個線程做爲客戶端鏈接回去並接下來開始履行客戶端線程的職責發送數據回去。ui

仍是繼續說監聽者線程返回的對端的套接字吧,接着監聽者線程會將存儲此套接字信息的thread_Settings*類型的變量做爲參數生成一個服務端線程進行後續數據的接收,監聽者繼續監聽新的客戶端的鏈接,而後繼續生成一個服務端線程,而後再繼續監聽,生成的若干服務端線程則努力地在一邊同時進行着數據的接收,別忘了還有報告者線程一直存在着,報告者線程從一開始就報告着各類信息,開始時是設置類型的信息,接着是鏈接類型的信息,後面就爲服務端線程打印一大堆的傳輸類型信息,若是服務端線程中有同一客戶端鏈接過來的狀況出現,那麼報告者線程還得打印多播類型的信息,這就得使用到MultiReport且其在控制檯表現的關鍵字爲"[SUM]......"url

 

 

截圖中紅色畫框標記出來的內容即爲多播類型的統計信息,在客戶端鏈接時添加 -P 選項再加上大於1的選項值就會在客戶端和服務端出現這種狀況。spa

 

而上面這張截圖中,紅色畫框的數據就有點詭異了,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是「年代已久」的時間),由於擺脫不了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,那麼就不進行該間隔時間段的統計信息的打印。

相關文章
相關標籤/搜索