webbench網站測壓工具源碼分析

来源:https://www.cnblogs.com/lanshanxiao/archive/2019/10/10/11651265.html
-Advertisement-
Play Games

1 /* 2 * (C) Radim Kolar 1997-2004 3 * This is free software, see GNU Public License version 2 for 4 * details. 5 * 6 * Simple forking WWW Server benc... ...


  1 /*
  2  * (C) Radim Kolar 1997-2004
  3  * This is free software, see GNU Public License version 2 for
  4  * details.
  5  *
  6  * Simple forking WWW Server benchmark:
  7  *
  8  * Usage:
  9  *   webbench --help
 10  *
 11  * Return codes:
 12  *    0 - sucess
 13  *    1 - benchmark failed (server is not on-line)
 14  *    2 - bad param
 15  *    3 - internal error, fork failed
 16  * 
 17  */ 
 18 #include "socket.c"
 19 #include <unistd.h>
 20 #include <sys/param.h>
 21 #include <rpc/types.h>
 22 #include <getopt.h>
 23 #include <strings.h>
 24 #include <time.h>
 25 #include <signal.h>
 26 
 27 /* values */
 28 volatile int timerexpired=0;//判斷測壓市場是否已經達到設定的時間
 29 int speed=0;//記錄進程成功得到伺服器相應的數量
 30 int failed=0;//記錄失敗的數量(speed表示成功數,failed表示失敗數)
 31 int bytes=0;//記錄進程成功讀取的位元組數
 32 /* globals */
 33 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 *///HTTP版本
 34 /* Allow: GET, HEAD, OPTIONS, TRACE */
 35 #define METHOD_GET 0 
 36 #define METHOD_HEAD 1 
 37 #define METHOD_OPTIONS 2
 38 #define METHOD_TRACE 3
 39 #define PROGRAM_VERSION "1.5"
 40 int method=METHOD_GET;//定義HTTP請求方法:預設方式GET請求
 41 int clients=1;//併發數目,預設只有一個進程發送請求,通過 -c 參數設置
 42 int force=0;//是否需要等待讀取從server返回的數據,0表示要等待讀取
 43 int force_reload=0;//是否使用緩存,1表示不緩存,0表示可以緩存頁面
 44 int proxyport=80;//代理伺服器的埠
 45 char *proxyhost=NULL;//代理伺服器的埠
 46 int benchtime=30;//測壓時間,預設30秒,通過 -t 參數設置
 47 /* internal */
 48 int mypipe[2];//使用管道進行父進程和子進程的通信
 49 char host[MAXHOSTNAMELEN];//伺服器端IP
 50 #define REQUEST_SIZE 2048
 51 char request[REQUEST_SIZE];//發送HTTP請求的內容
 52 
 53 static const struct option long_options[]=
 54 {
 55  {"force",no_argument,&force,1},
 56  {"reload",no_argument,&force_reload,1},
 57  {"time",required_argument,NULL,'t'},
 58  {"help",no_argument,NULL,'?'},
 59  {"http09",no_argument,NULL,'9'},
 60  {"http10",no_argument,NULL,'1'},
 61  {"http11",no_argument,NULL,'2'},
 62  {"get",no_argument,&method,METHOD_GET},
 63  {"head",no_argument,&method,METHOD_HEAD},
 64  {"options",no_argument,&method,METHOD_OPTIONS},
 65  {"trace",no_argument,&method,METHOD_TRACE},
 66  {"version",no_argument,NULL,'V'},
 67  {"proxy",required_argument,NULL,'p'},
 68  {"clients",required_argument,NULL,'c'},
 69  {NULL,0,NULL,0}
 70 };
 71 
 72 /* prototypes */
 73 static void benchcore(const char* host,const int port, const char *request);
 74 static int bench(void);
 75 static void build_request(const char *url);
 76 
 77 /*
 78      webbench在運行時可以設定壓測的持續時間,以秒為單位。
 79      例如我們希望測試30秒,也就意味著壓測30秒後程式應該退出了。
 80      webbench中使用信號(signal)來控製程序結束。
 81      函數1是在到達結束時間時運行的信號處理函數。
 82      它僅僅是將一個記錄是否超時的變數timerexpired標記為true。
 83      後面會看到,在程式的while迴圈中會不斷檢測此值,
 84      只有timerexpired=1,程式才會跳出while迴圈並返回。
 85 */
 86 static void alarm_handler(int signal)
 87 {
 88    timerexpired=1;
 89 }    
 90 
 91 /*
 92     教你如何使用webbench的函數,
 93     在linux命令行調用webbench方法不對的時候運行,作為提示。
 94     有一些比較常用的,比如-c來指定併發進程的多少;
 95     -t指定壓測的時間,以秒為單位;
 96     支持HTTP0.9,HTTP1.0,HTTP1.1三個版本;
 97     支持GET,HEAD,OPTIONS,TRACE四種請求方式。
 98     不要忘了調用時,命令行最後還應該附上要測的服務端URL。
 99 */
100 static void usage(void)
101 {
102    fprintf(stderr,
103     "webbench [option]... URL\n"
104     "  -f|--force               Don't wait for reply from server.\n"
105     "  -r|--reload              Send reload request - Pragma: no-cache.\n"
106     "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
107     "  -p|--proxy <server:port> Use proxy server for request.\n"
108     "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
109     "  -9|--http09              Use HTTP/0.9 style requests.\n"
110     "  -1|--http10              Use HTTP/1.0 protocol.\n"
111     "  -2|--http11              Use HTTP/1.1 protocol.\n"
112     "  --get                    Use GET request method.\n"
113     "  --head                   Use HEAD request method.\n"
114     "  --options                Use OPTIONS request method.\n"
115     "  --trace                  Use TRACE request method.\n"
116     "  -?|-h|--help             This information.\n"
117     "  -V|--version             Display program version.\n"
118     );
119 };
120 int main(int argc, char *argv[])
121 {
122  int opt=0;
123  int options_index=0;
124  char *tmp=NULL;
125 
126  if(argc==1)
127  {
128       usage();
129           return 2;
130  } 
131 
132  while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
133  {
134   switch(opt)
135   {
136    case  0 : break;
137    case 'f': force=1;break;
138    case 'r': force_reload=1;break; 
139    case '9': http10=0;break;
140    case '1': http10=1;break;
141    case '2': http10=2;break;
142    case 'V': printf(PROGRAM_VERSION"\n");exit(0);
143    /**
144        *C 庫函數 int atoi(const char *str) 把參數 str 所指向的字元串轉換為一個整數(類型為 int 型)
145        *int atoi(const char *str)
146        *str -- 要轉換為整數的字元串。
147        *該函數返迴轉換後的長整數,如果沒有執行有效的轉換,則返回零。
148        */
149    case 't': benchtime=atoi(optarg);break;         
150    case 'p': 
151          /* proxy server parsing server:port */
152            /**
153            *strrchr() 函數用於查找某字元在字元串中最後一次出現的位置,其原型為:
154         *char * strrchr(const char *str, int c);
155         *【參數】str 為要查找的字元串,c 為要查找的字元。
156         *strrchr() 將會找出 str 字元串中最後一次出現的字元 c 的地址,然後將該地址返回。
157         *註意:字元串 str 的結束標誌 NUL 也會被納入檢索範圍,所以 str 的組後一個字元也可以被定位。
158         *【返回值】如果找到就返回該字元最後一次出現的位置,否則返回 NULL。
159         *返回的地址是字元串在記憶體中隨機分配的地址再加上你所搜索的字元在字元串位置。設字元在字元串中首次出現的位置為 i,那麼返回的地址可以理解為 str + i。
160            */
161            /**
162            *optarg : char *optarg;  //選項的參數指針
163            */
164          tmp=strrchr(optarg,':');
165          proxyhost=optarg;
166          if(tmp==NULL)
167          {
168              break;
169          }
170          if(tmp==optarg)
171          {
172              fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
173              return 2;
174          }
175          /**
176          *C 庫函數 size_t strlen(const char *str) 計算字元串 str 的長度,直到空結束字元,但不包括空結束字元。
177          *size_t strlen(const char *str)
178          *參數:str -- 要計算長度的字元串。
179          *該函數返回字元串的長度。
180          */
181          if(tmp==optarg+strlen(optarg)-1)
182          {
183              fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
184              return 2;
185          }
186          *tmp='\0';//把:替換為\0
187          //從\0之後到\0之前
188          proxyport=atoi(tmp+1);break;
189    case ':':
190    case 'h':
191    case '?': usage();return 2;break;
192    case 'c': clients=atoi(optarg);break;
193   }
194  }
195  //int optind:argv的當前索引值。當getopt函數在while迴圈中使用時,剩下的字元串為操作數,下標從optind到argc-1
196  //argc,argv 參考:https://www.cnblogs.com/lanshanxiao/p/11568037.html
197  //getopt_long()中的函數,參考:https://www.cnblogs.com/xhg940420/p/7016574.html
198  //掃描完畢後,optind指向非長選項和非短選項和非參數的欄位,這裡應該指向URL
199  if(optind==argc) {
200                       fprintf(stderr,"webbench: Missing URL!\n");
201               usage();
202               return 2;
203                     }
204 
205  if(clients==0) clients=1;
206  if(benchtime==0) benchtime=60;
207  /* Copyright */
208  fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
209      "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
210      );
211  /**
212   *命令讀取完成後,argv[optind]中應該存放著URL,
213   *建立完整的http請求,http請求存放在變數char request[REQUEST_SIZE]中
214   */
215  build_request(argv[optind]);
216  /* print bench info *///輸出平臺信息
217  printf("\nBenchmarking: ");
218  switch(method)
219  {
220      case METHOD_GET:
221      default:
222          printf("GET");break;
223      case METHOD_OPTIONS:
224          printf("OPTIONS");break;
225      case METHOD_HEAD:
226          printf("HEAD");break;
227      case METHOD_TRACE:
228          printf("TRACE");break;
229  }
230  printf(" %s",argv[optind]);//列印出URL
231  switch(http10)
232  {
233      case 0: printf(" (using HTTP/0.9)");break;
234      case 2: printf(" (using HTTP/1.1)");break;
235  }
236  printf("\n");
237  if(clients==1) printf("1 client");
238  else
239    printf("%d clients",clients);
240 
241  printf(", running %d sec", benchtime);
242  if(force) printf(", early socket close");
243  if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
244  if(force_reload) printf(", forcing reload");
245  printf(".\n");
246  //壓力測試最後一句話,所有的壓力測試都在bench函數中實現
247  return bench();
248 }
249 
250 /*
251     函數主要操作全局變數char request[REQUEST_SIZE],根據url填充其內容。
252     典型的HTTP的GET請求:
253     GET /test.jpg HTTP/1.1
254     User-Agent: WebBench 1.5
255     Host:192.168.10.1
256     Pragma: no-cache
257     Connection: close
258 
259     build_request函數的目的就是要把
260     類似於以上這一大坨信息全部存到全局變數request[REQUEST_SIZE]中,
261     其中換行操作使用的是”\r\n”。
262     而以上這一大坨信息的具體內容是要根據命令行輸入的參數,以及url來確定的。
263     該函數使用了大量的字元串操作函數,
264     例如strcpy,strstr,strncasecmp,strlen,strchr,index,strncpy,strcat。
265     對這些基礎函數不太熟悉的同學可以借這個函數複習一下。
266 */
267 void build_request(const char *url)
268 {
269   char tmp[10];
270   int i;
271 
272   bzero(host,MAXHOSTNAMELEN);//bzero():置host(位元組字元串)前MAXHOSTNAMELEN個位元組為0,包括'\0')
273   bzero(request,REQUEST_SIZE);
274   
275   if(force_reload && proxyhost!=NULL && http10<1) http10=1;//滿足一定條件,更換HTTP協議
276   if(method==METHOD_HEAD && http10<1) http10=1;
277   if(method==METHOD_OPTIONS && http10<2) http10=2;
278   if(method==METHOD_TRACE && http10<2) http10=2;
279 
280   switch(method)
281   {
282       default:
283       //strcpy() 函數用於對字元串進行複製(拷貝)。
284       //char* strcpy(char* strDestination, const char* strSource);
285       //strSource 指向的字元串複製到 strDestination
286       case METHOD_GET: strcpy(request,"GET");break;
287       case METHOD_HEAD: strcpy(request,"HEAD");break;
288       case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
289       case METHOD_TRACE: strcpy(request,"TRACE");break;
290   }
291 
292   //char*strcat(char* strDestination, const char* strSource);
293   /*
294       strcat() 函數用來將兩個字元串連接(拼接)起來。
295       strcat() 函數把 strSource 所指向的字元串追加到 strDestination 所指向的字元串的結尾,
296       所以必須要保證 strDestination 有足夠的記憶體空間來容納兩個字元串,否則會導致溢出錯誤。
297       註意:strDestination 末尾的\0會被覆蓋,strSource 末尾的\0會一起被覆制過去,最終的字元串只有一個\0。
298   */
299   strcat(request," ");
300 
301   /*
302     char *strstr(const char *haystack, const char *needle) 
303     在字元串 haystack 中查找第一次出現字元串 needle 的位置,不包含終止符 '\0'。
304     該函數返回在 haystack 中第一次出現 needle 的地址,如果未找到則返回 null。
305   */
306   if(NULL==strstr(url,"://"))
307   {
308       fprintf(stderr, "\n%s: is not a valid URL.\n",url);
309       exit(2);
310   }
311 
312   /*
313       strlen(char *);
314       檢測字元串實際長度。
315       strlen(char *)檢測的是'\0',strlen(char *)碰到'\0'就返回'\0'以前的字元數(不包括'\0')。
316       strlen(char*)函數求的是字元串的實際長度,它求得方法是從開始到遇到第一個'\0',
317       如果你只定義沒有給它賦初值,這個結果是不定的,它會從aa首地址一直找下去,直到遇到'\0'停止。
318   */
319   if(strlen(url)>1500)
320   {
321      fprintf(stderr,"URL is too long.\n");
322      exit(2);
323   }
324   if(proxyhost==NULL)
325          /*
326                  int strncasecmp(const char *s1, const char *s2, size_t n);
327                  strncasecmp()用來比較參數s1 和s2 字元串前n個字元,比較時會自動忽略大小寫的差異。
328                  若參數s1 和s2 字元串相同則返回0。s1 若大於s2 則返回大於0 的值,s1 若小於s2 則返回小於0 的值。
329          */
330        if (0!=strncasecmp("http://",url,7)) 
331        { 
332                fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
333             exit(2);
334        }
335   /* protocol/host delimiter */
336   i=strstr(url,"://")-url+3;
337   /* printf("%d\n",i); */
338 
339   /*
340     char *strchr(const char *str, char c) 
341     該函數返回在字元串 str 中第一次出現字元 c 的地址,如果未找到該字元則返回 NULL。
342   */
343   if(strchr(url+i,'/')==NULL) {
344                                 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
345                                 exit(2);
346                               }
347   if(proxyhost==NULL)
348   {
349    /* get port from hostname */
350    if(index(url+i,':')!=NULL &&
351       index(url+i,':')<index(url+i,'/'))
352    {
353           /*
354             char * strncpy(char *s1,char *s2,size_t n);
355               將字元串s2中最多n個字元複製到字元數組s1中,返回指向s1的指針。
356               註意:如果源串長度大於n,則strncpy不複製最後的'\0'結束符,
357             所以是不安全的,複製完後需要手動添加字元串的結束符才行。
358            */
359        strncpy(host,url+i,strchr(url+i,':')-url-i);
360        bzero(tmp,10);
361        strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
362        /* printf("tmp=%s\n",tmp); */
363 
364        /*
365             C語言庫函數名: atoi
366               功 能: 把字元串轉換成整型數.
367               名字來源:array to integer 的縮寫.
368               函數說明: atoi()會掃描參數nptr字元串,如果第一個字元不是數字也不是正負號返回零,
369             否則開始做類型轉換,之後檢測到非數字或結束符 \0 時停止轉換,返回整型數。
370               原型: int atoi(const char *nptr);
371        */
372        proxyport=atoi(tmp);
373        if(proxyport==0) proxyport=80;
374    } else
375    {
376         /*
377         size_t strcspn(const char *s, const char * reject);
378         函數說明:strcspn()從參數s 字元串的開頭計算連續的字元,
379         而這些字元都完全不在參數reject 所指的字元串中。
380         簡單地說, 若strcspn()返回的數值為n,則代表字元串s 開頭連續有n 個字元都不含字元串reject 內的字元。
381         返回值:返回字元串s 開頭連續不含字元串reject 內的字元數目。
382          */
383      strncpy(host,url+i,strcspn(url+i,"/"));
384    }
385    // printf("Host=%s\n",host);
386    strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
387   } else
388   {
389    // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
390    strcat(request,url);
391   }
392   if(http10==1)

              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、pycharm創建ini格式的文件,沒有對應的 ini 文件類型 需要更新 Ini 2、setting–>marketplace 搜索 Ini ,然後進行安裝,重啟pycharm 3、重啟pycharm後,之前創建的 ini 文件會自動更改為 正確的 ini 文件圖標 ...
  • (手機橫屏看源碼更方便) 問題 (1)自己動手寫的線程池如何支持帶返回值的任務呢? (2)如果任務執行的過程中拋出異常了該怎麼處理呢? 簡介 上一章我們自己動手寫了一個線程池,但是它是不支持帶返回值的任務的,那麼,我們自己能否實現呢?必須可以,今天我們就一起來實現帶返回值任務的線程池。 前情回顧 首 ...
  • 靜態變數 有時候當我們希望某個數據在記憶體之中只有一份,而且能被一個類的所有實例對象所共用的時候。我們可以用static來修飾成員變數,該變數叫靜態變數。 例 static String schoolName; 靜態變數不會被回收 該變數可以用 類名.變數名調用,也可以通過實例對象調用 static ...
  • 一、Python--shutil模塊介紹:高級的 文件、文件夾、壓縮包 處理模塊,導入import shutil 二、基本操作 2.1、shutil.copyfileobj(fsrc, fdst[, length])將文件內容拷貝到另一個文件中 2.2、shutil.copyfile(src, ds ...
  • 一、self的位置是出現在哪裡? 首先,self是在類的方法中的,在調用此方法時,不用給self賦值,Python會自動給他賦值,而且這個值就是類的實例--對象本身。也可以將self換成別的叫法例如seef,但不建議,因為大家習慣也預設了寫成self。 二、self的值是什麼? self的值是Pyt ...
  • 前段時間因為項目需要,研究了一下在 Windows 系統下進行 PHP 擴展的開發,對於 PHP 擴展的開發並不是一件容易的事情(話又說回來了,會者不難,難者不會,關鍵是自己不會)。我當時的需求,主要是通過 PHP 擴展來載入 DLL 文件,並調用 DLL 中的導出函數。由於以前有一些 Win32 ...
  • Java基本知識、jvm深入瞭解 spring,springmvc,springboot 微服務springcloud ,dubbo 、分散式、集群、負載均衡 redis, rabbitmq,kafka、mogodb、mysql、shardingjdbc、mycat tcp協議、http協議、web... ...
  • Spring Boot是由Pivotal團隊提供的全新框架,設計目的是用來簡化新Spring應用的初始搭建以及開發過程。它主要推崇的是'消滅配置’,實現零配置。 下麵就介紹一下如何使用idea快速搭建Springboot項目。 一、點擊最上角File-New-Project。 二、選擇創建Sprin ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...