Webbench源代码分析(转载)

时间:2022-03-31 23:20:27

转载地址 http://blog.csdn.net/kangroger/article/details/42500703

Web Bench是一个网站压力测试的工具。其最后更新时间是2004年,已经十年多了。其源代码总共才500多行,全部使用C语言编写,最多可以模拟上万个并发连接。

其原理也比较简单,就是使用fork创建子进程,通过子进程来测试http连接,把测试结果写到管道,再有父进程读取管道信息来计算测试结果。流程图下:

Webbench源代码分析(转载)

其源代码有2个文件组成

socket.c是创建socket连接的。主要的代码在webbench.c中。

webbench.c中有几个主要的函数。

static void usage(void)是在使用出错时提示怎么使用本程序。

void build_request(const char *url)是用来创建http连接请求的。

static int bench(void)中创建管道和子进程,调用测试http函数。

void benchcore(const char *host,const int port,const char *req)对http请求进行测试。

socket.c源代码及注释:

[cpp] view plain copy

Webbench源代码分析(转载)Webbench源代码分析(转载)

  1. /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
  2. *
  3. * This module has been modified by Radim Kolar for OS/2 emx
  4. */
  5. /***********************************************************************
  6. module:       socket.c
  7. program:      popclient
  8. SCCS ID:      @(#)socket.c    1.5  4/1/94
  9. programmer:   Virginia Tech Computing Center
  10. compiler:     DEC RISC C compiler (Ultrix 4.1)
  11. environment:  DEC Ultrix 4.3
  12. description:  UNIX sockets code.
  13. ***********************************************************************/
  14. #include <sys/types.h>
  15. #include <sys/socket.h>
  16. #include <fcntl.h>
  17. #include <netinet/in.h>
  18. #include <arpa/inet.h>
  19. #include <netdb.h>
  20. #include <sys/time.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <stdarg.h>
  26. /***********
  27. 功能:通过地址和端口建立网络连接
  28. @host:网络地址
  29. @clientPort:端口
  30. Return:建立的socket连接。
  31. 如果返回-1,表示建立连接失败
  32. ************/
  33. int Socket(const char *host, int clientPort)
  34. {
  35. int sock;
  36. unsigned long inaddr;
  37. struct sockaddr_in ad;
  38. struct hostent *hp;
  39. memset(&ad, 0, sizeof(ad));
  40. ad.sin_family = AF_INET;
  41. inaddr = inet_addr(host);//将点分的十进制的IP转为无符号长整形
  42. if (inaddr != INADDR_NONE)
  43. memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
  44. else//如果host是域名
  45. {
  46. hp = gethostbyname(host);//用域名获取IP
  47. if (hp == NULL)
  48. return -1;
  49. memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
  50. }
  51. ad.sin_port = htons(clientPort);
  52. sock = socket(AF_INET, SOCK_STREAM, 0);
  53. if (sock < 0)
  54. return sock;
  55. //连接
  56. if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
  57. return -1;
  58. return sock;
  59. }

wenbench.c源代码及注释

[cpp] view plain copy

Webbench源代码分析(转载)Webbench源代码分析(转载)

  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. /* values */
  27. volatile int timerexpired=0;
  28. int speed=0;
  29. int failed=0;
  30. int bytes=0;
  31. /* globals */
  32. //http协议的版本号
  33. int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
  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方法
  41. int clients=1;//并发数
  42. int force=0;//是否等待服务器应答。默认为不等待
  43. int force_reload=0;
  44. int proxyport=80;//代理服务器端口号。默认为80
  45. char *proxyhost=NULL;//代理服务器的地址
  46. int benchtime=30;//运行多久。默认为30s。可以通过-t指定
  47. /* internal */
  48. int mypipe[2];
  49. char host[MAXHOSTNAMELEN];
  50. #define REQUEST_SIZE 2048
  51. char request[REQUEST_SIZE];
  52. //struct option结构体,配合getopt_long函数使用
  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. /* prototypes */
  72. static void benchcore(const char* host,const int port, const char *request);
  73. static int bench(void);
  74. static void build_request(const char *url);
  75. static void alarm_handler(int signal)
  76. {
  77. timerexpired=1;
  78. }
  79. /**************
  80. 程序使用方法
  81. ***************/
  82. static void usage(void)
  83. {
  84. fprintf(stderr,
  85. "webbench [option]... URL\n"
  86. "  -f|--force               Don't wait for reply from server.\n"
  87. "  -r|--reload              Send reload request - Pragma: no-cache.\n"
  88. "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
  89. "  -p|--proxy <server:port> Use proxy server for request.\n"
  90. "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
  91. "  -9|--http09              Use HTTP/0.9 style requests.\n"
  92. "  -1|--http10              Use HTTP/1.0 protocol.\n"
  93. "  -2|--http11              Use HTTP/1.1 protocol.\n"
  94. "  --get                    Use GET request method.\n"
  95. "  --head                   Use HEAD request method.\n"
  96. "  --options                Use OPTIONS request method.\n"
  97. "  --trace                  Use TRACE request method.\n"
  98. "  -?|-h|--help             This information.\n"
  99. "  -V|--version             Display program version.\n"
  100. );
  101. };
  102. int main(int argc, char *argv[])
  103. {
  104. int opt=0;
  105. int options_index=0;
  106. char *tmp=NULL;
  107. if(argc==1)//使用方法不合适
  108. {
  109. usage();
  110. return 2;
  111. }
  112. //检查输入参数,并设置对应选项
  113. while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
  114. {
  115. switch(opt)
  116. {
  117. case  0 : break;
  118. case 'f': force=1;break;
  119. case 'r': force_reload=1;break;
  120. case '9': http10=0;break;
  121. case '1': http10=1;break;
  122. case '2': http10=2;break;
  123. case 'V': printf(PROGRAM_VERSION"\n");exit(0);//输入版本号
  124. case 't': benchtime=atoi(optarg);break;  //optarg表示命令后的参数,例如-c 100,optarg为100。
  125. //atoi表示把字符串转换成长整型。
  126. case 'p':
  127. /* proxy server parsing server:port */
  128. tmp=strrchr(optarg,':');
  129. proxyhost=optarg;//设定地址
  130. if(tmp==NULL)
  131. {
  132. break;
  133. }
  134. if(tmp==optarg)
  135. {
  136. fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
  137. return 2;
  138. }
  139. if(tmp==optarg+strlen(optarg)-1)
  140. {
  141. fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
  142. return 2;
  143. }
  144. *tmp='\0';
  145. proxyport=atoi(tmp+1);break;//重设端口号
  146. case ':':
  147. case 'h':
  148. case '?': usage();return 2;break;
  149. case 'c': clients=atoi(optarg);break;//并发数
  150. }
  151. }
  152. //optind为对应参数的下标位置
  153. if(optind==argc) {
  154. fprintf(stderr,"webbench: Missing URL!\n");
  155. usage();
  156. return 2;
  157. }
  158. if(clients==0) clients=1;
  159. if(benchtime==0) benchtime=60;
  160. /* Copyright */
  161. fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
  162. "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
  163. );
  164. //使用中,最后为URL,所以optind应该是URL的位置
  165. build_request(argv[optind]);
  166. /* print bench info */
  167. printf("\nBenchmarking: ");
  168. switch(method)
  169. {
  170. case METHOD_GET:
  171. default:
  172. printf("GET");break;
  173. case METHOD_OPTIONS:
  174. printf("OPTIONS");break;
  175. case METHOD_HEAD:
  176. printf("HEAD");break;
  177. case METHOD_TRACE:
  178. printf("TRACE");break;
  179. }
  180. printf(" %s",argv[optind]);
  181. switch(http10)
  182. {
  183. case 0: printf(" (using HTTP/0.9)");break;
  184. case 2: printf(" (using HTTP/1.1)");break;
  185. }
  186. printf("\n");
  187. if(clients==1) printf("1 client");
  188. else
  189. printf("%d clients",clients);
  190. printf(", running %d sec", benchtime);
  191. if(force) printf(", early socket close");
  192. if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
  193. if(force_reload) printf(", forcing reload");
  194. printf(".\n");
  195. return bench();
  196. }
  197. /****************
  198. 创建URL请求连接
  199. @url:url地址
  200. 创建好的请求放在全局变量request中
  201. ****************/
  202. void build_request(const char *url)
  203. {
  204. char tmp[10];
  205. int i;
  206. //请求地址和请求连接清零
  207. bzero(host,MAXHOSTNAMELEN);
  208. bzero(request,REQUEST_SIZE);
  209. if(force_reload && proxyhost!=NULL && http10<1) http10=1;
  210. if(method==METHOD_HEAD && http10<1) http10=1;
  211. if(method==METHOD_OPTIONS && http10<2) http10=2;
  212. if(method==METHOD_TRACE && http10<2) http10=2;
  213. switch(method)
  214. {
  215. default:
  216. case METHOD_GET: strcpy(request,"GET");break;
  217. case METHOD_HEAD: strcpy(request,"HEAD");break;
  218. case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
  219. case METHOD_TRACE: strcpy(request,"TRACE");break;
  220. }
  221. strcat(request," ");
  222. if(NULL==strstr(url,"://"))//找“://”在URL中的位置
  223. {
  224. fprintf(stderr, "\n%s: is not a valid URL.\n",url);
  225. exit(2);
  226. }
  227. if(strlen(url)>1500)//url是否太长
  228. {
  229. fprintf(stderr,"URL is too long.\n");
  230. exit(2);
  231. }
  232. if(proxyhost==NULL)//代理服务器是否为空
  233. if (0!=strncasecmp("http://",url,7)) //比较前7个字符串
  234. { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
  235. exit(2);
  236. }
  237. /* protocol/host delimiter */
  238. i=strstr(url,"://")-url+3;//i指向http://后第一个位置
  239. /* printf("%d\n",i); */
  240. if(strchr(url+i,'/')==NULL) {
  241. fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
  242. exit(2);
  243. }
  244. if(proxyhost==NULL)
  245. {
  246. /* get port from hostname */
  247. if(index(url+i,':')!=NULL &&
  248. index(url+i,':')<index(url+i,'/'))//判断url中是否指定了端口号
  249. {
  250. strncpy(host,url+i,strchr(url+i,':')-url-i);//取出主机地址
  251. bzero(tmp,10);
  252. strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
  253. /* printf("tmp=%s\n",tmp); */
  254. proxyport=atoi(tmp);//端口号转换为int
  255. if(proxyport==0) proxyport=80;
  256. } else
  257. {
  258. strncpy(host,url+i,strcspn(url+i,"/"));
  259. }
  260. // printf("Host=%s\n",host);
  261. strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
  262. } else
  263. {
  264. // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
  265. strcat(request,url);
  266. }
  267. if(http10==1)//版本号
  268. strcat(request," HTTP/1.0");
  269. else if (http10==2)
  270. strcat(request," HTTP/1.1");
  271. strcat(request,"\r\n");
  272. if(http10>0)
  273. strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
  274. if(proxyhost==NULL && http10>0)
  275. {
  276. strcat(request,"Host: ");
  277. strcat(request,host);
  278. strcat(request,"\r\n");
  279. }
  280. if(force_reload && proxyhost!=NULL)
  281. {
  282. strcat(request,"Pragma: no-cache\r\n");
  283. }
  284. if(http10>1)
  285. strcat(request,"Connection: close\r\n");
  286. /* add empty line at end */
  287. if(http10>0) strcat(request,"\r\n");
  288. // printf("Req=%s\n",request);
  289. }
  290. /* vraci system rc error kod */
  291. /**********************
  292. 创建管道和子进程,对http请求进行测试
  293. **********************/
  294. static int bench(void)
  295. {
  296. int i,j,k;
  297. pid_t pid=0;
  298. FILE *f;
  299. /* check avaibility of target server */
  300. i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
  301. if(i<0) {
  302. fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
  303. return 1;
  304. }
  305. close(i);
  306. /* create pipe */
  307. if(pipe(mypipe))
  308. {
  309. perror("pipe failed.");
  310. return 3;
  311. }
  312. /* not needed, since we have alarm() in childrens */
  313. /* wait 4 next system clock tick */
  314. /*
  315. cas=time(NULL);
  316. while(time(NULL)==cas)
  317. sched_yield();
  318. */
  319. /* fork childs */
  320. for(i=0;i<clients;i++)
  321. {
  322. pid=fork();
  323. if(pid <= (pid_t) 0)
  324. {
  325. /* child process or error*/
  326. sleep(1); /* make childs faster */
  327. break;
  328. }
  329. }
  330. if( pid< (pid_t) 0)
  331. {
  332. fprintf(stderr,"problems forking worker no. %d\n",i);
  333. perror("fork failed.");
  334. return 3;
  335. }
  336. if(pid== (pid_t) 0)
  337. {
  338. /* I am a child */
  339. if(proxyhost==NULL)//是否使用proxyhost
  340. benchcore(host,proxyport,request);
  341. else
  342. benchcore(proxyhost,proxyport,request);
  343. /* write results to pipe */
  344. f=fdopen(mypipe[1],"w");
  345. if(f==NULL)
  346. {
  347. perror("open pipe for writing failed.");
  348. return 3;
  349. }
  350. /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
  351. fprintf(f,"%d %d %d\n",speed,failed,bytes);//把每个子进程运行的结果放到管道。
  352. fclose(f);
  353. return 0;
  354. } else
  355. {
  356. f=fdopen(mypipe[0],"r");
  357. if(f==NULL)
  358. {
  359. perror("open pipe for reading failed.");
  360. return 3;
  361. }
  362. setvbuf(f,NULL,_IONBF,0);
  363. speed=0;
  364. failed=0;
  365. bytes=0;
  366. while(1)//父进程读取管道数据,并做加法
  367. {
  368. pid=fscanf(f,"%d %d %d",&i,&j,&k);
  369. if(pid<2)
  370. {
  371. fprintf(stderr,"Some of our childrens died.\n");
  372. break;
  373. }
  374. speed+=i;
  375. failed+=j;
  376. bytes+=k;
  377. /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
  378. if(--clients==0) break;
  379. }
  380. fclose(f);
  381. //输出测试结果
  382. printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
  383. (int)((speed+failed)/(benchtime/60.0f)),
  384. (int)(bytes/(float)benchtime),
  385. speed,
  386. failed);
  387. }
  388. return i;
  389. }
  390. /******************************
  391. 这里才是测试http的地方
  392. @host:地址
  393. @port:端口
  394. @req:http格式方法
  395. ********************************/
  396. void benchcore(const char *host,const int port,const char *req)
  397. {
  398. int rlen;
  399. char buf[1500];
  400. int s,i;
  401. struct sigaction sa;
  402. /* setup alarm signal handler */
  403. sa.sa_handler=alarm_handler;//定时器方法
  404. sa.sa_flags=0;
  405. if(sigaction(SIGALRM,&sa,NULL))
  406. exit(3);
  407. alarm(benchtime);
  408. rlen=strlen(req);
  409. nexttry:while(1)
  410. {
  411. if(timerexpired)//定时器到时后,会设定timerexpired=1,函数就会返回
  412. {
  413. if(failed>0)
  414. {
  415. /* fprintf(stderr,"Correcting failed by signal\n"); */
  416. failed--;
  417. }
  418. return;
  419. }
  420. s=Socket(host,port);      //创建连接
  421. if(s<0) { failed++;continue;} //连接失败,failed加1
  422. if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}//发送失败
  423. if(http10==0)
  424. if(shutdown(s,1)) { failed++;close(s);continue;}
  425. if(force==0) //force等于0表示要读取http请求数据
  426. {
  427. /* read all available data from socket */
  428. while(1)
  429. {
  430. if(timerexpired) break;
  431. i=read(s,buf,1500);
  432. /* fprintf(stderr,"%d\n",i); */
  433. if(i<0)
  434. {
  435. failed++;
  436. close(s);
  437. goto nexttry;
  438. }
  439. else
  440. if(i==0) break;
  441. else
  442. bytes+=i;//读取字节数增加
  443. }
  444. }
  445. if(close(s)) {failed++;continue;}
  446. speed++;//http测试成功一次,speed加1
  447. }
  448. }

Web Bench是一个网站压力测试的工具。其最后更新时间是2004年,已经十年多了。其源代码总共才500多行,全部使用C语言编写,最多可以模拟上万个并发连接。

其原理也比较简单,就是使用fork创建子进程,通过子进程来测试http连接,把测试结果写到管道,再有父进程读取管道信息来计算测试结果。流程图下:

Webbench源代码分析(转载)

其源代码有2个文件组成

socket.c是创建socket连接的。主要的代码在webbench.c中。

webbench.c中有几个主要的函数。

static void usage(void)是在使用出错时提示怎么使用本程序。

void build_request(const char *url)是用来创建http连接请求的。

static int bench(void)中创建管道和子进程,调用测试http函数。

void benchcore(const char *host,const int port,const char *req)对http请求进行测试。

socket.c源代码及注释:

[cpp] view plain copy

Webbench源代码分析(转载)Webbench源代码分析(转载)

  1. /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
  2. *
  3. * This module has been modified by Radim Kolar for OS/2 emx
  4. */
  5. /***********************************************************************
  6. module:       socket.c
  7. program:      popclient
  8. SCCS ID:      @(#)socket.c    1.5  4/1/94
  9. programmer:   Virginia Tech Computing Center
  10. compiler:     DEC RISC C compiler (Ultrix 4.1)
  11. environment:  DEC Ultrix 4.3
  12. description:  UNIX sockets code.
  13. ***********************************************************************/
  14. #include <sys/types.h>
  15. #include <sys/socket.h>
  16. #include <fcntl.h>
  17. #include <netinet/in.h>
  18. #include <arpa/inet.h>
  19. #include <netdb.h>
  20. #include <sys/time.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <stdarg.h>
  26. /***********
  27. 功能:通过地址和端口建立网络连接
  28. @host:网络地址
  29. @clientPort:端口
  30. Return:建立的socket连接。
  31. 如果返回-1,表示建立连接失败
  32. ************/
  33. int Socket(const char *host, int clientPort)
  34. {
  35. int sock;
  36. unsigned long inaddr;
  37. struct sockaddr_in ad;
  38. struct hostent *hp;
  39. memset(&ad, 0, sizeof(ad));
  40. ad.sin_family = AF_INET;
  41. inaddr = inet_addr(host);//将点分的十进制的IP转为无符号长整形
  42. if (inaddr != INADDR_NONE)
  43. memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
  44. else//如果host是域名
  45. {
  46. hp = gethostbyname(host);//用域名获取IP
  47. if (hp == NULL)
  48. return -1;
  49. memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
  50. }
  51. ad.sin_port = htons(clientPort);
  52. sock = socket(AF_INET, SOCK_STREAM, 0);
  53. if (sock < 0)
  54. return sock;
  55. //连接
  56. if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
  57. return -1;
  58. return sock;
  59. }

wenbench.c源代码及注释

[cpp] view plain copy

Webbench源代码分析(转载)Webbench源代码分析(转载)

  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. /* values */
  27. volatile int timerexpired=0;
  28. int speed=0;
  29. int failed=0;
  30. int bytes=0;
  31. /* globals */
  32. //http协议的版本号
  33. int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */
  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方法
  41. int clients=1;//并发数
  42. int force=0;//是否等待服务器应答。默认为不等待
  43. int force_reload=0;
  44. int proxyport=80;//代理服务器端口号。默认为80
  45. char *proxyhost=NULL;//代理服务器的地址
  46. int benchtime=30;//运行多久。默认为30s。可以通过-t指定
  47. /* internal */
  48. int mypipe[2];
  49. char host[MAXHOSTNAMELEN];
  50. #define REQUEST_SIZE 2048
  51. char request[REQUEST_SIZE];
  52. //struct option结构体,配合getopt_long函数使用
  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. /* prototypes */
  72. static void benchcore(const char* host,const int port, const char *request);
  73. static int bench(void);
  74. static void build_request(const char *url);
  75. static void alarm_handler(int signal)
  76. {
  77. timerexpired=1;
  78. }
  79. /**************
  80. 程序使用方法
  81. ***************/
  82. static void usage(void)
  83. {
  84. fprintf(stderr,
  85. "webbench [option]... URL\n"
  86. "  -f|--force               Don't wait for reply from server.\n"
  87. "  -r|--reload              Send reload request - Pragma: no-cache.\n"
  88. "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
  89. "  -p|--proxy <server:port> Use proxy server for request.\n"
  90. "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
  91. "  -9|--http09              Use HTTP/0.9 style requests.\n"
  92. "  -1|--http10              Use HTTP/1.0 protocol.\n"
  93. "  -2|--http11              Use HTTP/1.1 protocol.\n"
  94. "  --get                    Use GET request method.\n"
  95. "  --head                   Use HEAD request method.\n"
  96. "  --options                Use OPTIONS request method.\n"
  97. "  --trace                  Use TRACE request method.\n"
  98. "  -?|-h|--help             This information.\n"
  99. "  -V|--version             Display program version.\n"
  100. );
  101. };
  102. int main(int argc, char *argv[])
  103. {
  104. int opt=0;
  105. int options_index=0;
  106. char *tmp=NULL;
  107. if(argc==1)//使用方法不合适
  108. {
  109. usage();
  110. return 2;
  111. }
  112. //检查输入参数,并设置对应选项
  113. while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
  114. {
  115. switch(opt)
  116. {
  117. case  0 : break;
  118. case 'f': force=1;break;
  119. case 'r': force_reload=1;break;
  120. case '9': http10=0;break;
  121. case '1': http10=1;break;
  122. case '2': http10=2;break;
  123. case 'V': printf(PROGRAM_VERSION"\n");exit(0);//输入版本号
  124. case 't': benchtime=atoi(optarg);break;  //optarg表示命令后的参数,例如-c 100,optarg为100。
  125. //atoi表示把字符串转换成长整型。
  126. case 'p':
  127. /* proxy server parsing server:port */
  128. tmp=strrchr(optarg,':');
  129. proxyhost=optarg;//设定地址
  130. if(tmp==NULL)
  131. {
  132. break;
  133. }
  134. if(tmp==optarg)
  135. {
  136. fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
  137. return 2;
  138. }
  139. if(tmp==optarg+strlen(optarg)-1)
  140. {
  141. fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
  142. return 2;
  143. }
  144. *tmp='\0';
  145. proxyport=atoi(tmp+1);break;//重设端口号
  146. case ':':
  147. case 'h':
  148. case '?': usage();return 2;break;
  149. case 'c': clients=atoi(optarg);break;//并发数
  150. }
  151. }
  152. //optind为对应参数的下标位置
  153. if(optind==argc) {
  154. fprintf(stderr,"webbench: Missing URL!\n");
  155. usage();
  156. return 2;
  157. }
  158. if(clients==0) clients=1;
  159. if(benchtime==0) benchtime=60;
  160. /* Copyright */
  161. fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
  162. "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
  163. );
  164. //使用中,最后为URL,所以optind应该是URL的位置
  165. build_request(argv[optind]);
  166. /* print bench info */
  167. printf("\nBenchmarking: ");
  168. switch(method)
  169. {
  170. case METHOD_GET:
  171. default:
  172. printf("GET");break;
  173. case METHOD_OPTIONS:
  174. printf("OPTIONS");break;
  175. case METHOD_HEAD:
  176. printf("HEAD");break;
  177. case METHOD_TRACE:
  178. printf("TRACE");break;
  179. }
  180. printf(" %s",argv[optind]);
  181. switch(http10)
  182. {
  183. case 0: printf(" (using HTTP/0.9)");break;
  184. case 2: printf(" (using HTTP/1.1)");break;
  185. }
  186. printf("\n");
  187. if(clients==1) printf("1 client");
  188. else
  189. printf("%d clients",clients);
  190. printf(", running %d sec", benchtime);
  191. if(force) printf(", early socket close");
  192. if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
  193. if(force_reload) printf(", forcing reload");
  194. printf(".\n");
  195. return bench();
  196. }
  197. /****************
  198. 创建URL请求连接
  199. @url:url地址
  200. 创建好的请求放在全局变量request中
  201. ****************/
  202. void build_request(const char *url)
  203. {
  204. char tmp[10];
  205. int i;
  206. //请求地址和请求连接清零
  207. bzero(host,MAXHOSTNAMELEN);
  208. bzero(request,REQUEST_SIZE);
  209. if(force_reload && proxyhost!=NULL && http10<1) http10=1;
  210. if(method==METHOD_HEAD && http10<1) http10=1;
  211. if(method==METHOD_OPTIONS && http10<2) http10=2;
  212. if(method==METHOD_TRACE && http10<2) http10=2;
  213. switch(method)
  214. {
  215. default:
  216. case METHOD_GET: strcpy(request,"GET");break;
  217. case METHOD_HEAD: strcpy(request,"HEAD");break;
  218. case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
  219. case METHOD_TRACE: strcpy(request,"TRACE");break;
  220. }
  221. strcat(request," ");
  222. if(NULL==strstr(url,"://"))//找“://”在URL中的位置
  223. {
  224. fprintf(stderr, "\n%s: is not a valid URL.\n",url);
  225. exit(2);
  226. }
  227. if(strlen(url)>1500)//url是否太长
  228. {
  229. fprintf(stderr,"URL is too long.\n");
  230. exit(2);
  231. }
  232. if(proxyhost==NULL)//代理服务器是否为空
  233. if (0!=strncasecmp("http://",url,7)) //比较前7个字符串
  234. { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
  235. exit(2);
  236. }
  237. /* protocol/host delimiter */
  238. i=strstr(url,"://")-url+3;//i指向http://后第一个位置
  239. /* printf("%d\n",i); */
  240. if(strchr(url+i,'/')==NULL) {
  241. fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
  242. exit(2);
  243. }
  244. if(proxyhost==NULL)
  245. {
  246. /* get port from hostname */
  247. if(index(url+i,':')!=NULL &&
  248. index(url+i,':')<index(url+i,'/'))//判断url中是否指定了端口号
  249. {
  250. strncpy(host,url+i,strchr(url+i,':')-url-i);//取出主机地址
  251. bzero(tmp,10);
  252. strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
  253. /* printf("tmp=%s\n",tmp); */
  254. proxyport=atoi(tmp);//端口号转换为int
  255. if(proxyport==0) proxyport=80;
  256. } else
  257. {
  258. strncpy(host,url+i,strcspn(url+i,"/"));
  259. }
  260. // printf("Host=%s\n",host);
  261. strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
  262. } else
  263. {
  264. // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
  265. strcat(request,url);
  266. }
  267. if(http10==1)//版本号
  268. strcat(request," HTTP/1.0");
  269. else if (http10==2)
  270. strcat(request," HTTP/1.1");
  271. strcat(request,"\r\n");
  272. if(http10>0)
  273. strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
  274. if(proxyhost==NULL && http10>0)
  275. {
  276. strcat(request,"Host: ");
  277. strcat(request,host);
  278. strcat(request,"\r\n");
  279. }
  280. if(force_reload && proxyhost!=NULL)
  281. {
  282. strcat(request,"Pragma: no-cache\r\n");
  283. }
  284. if(http10>1)
  285. strcat(request,"Connection: close\r\n");
  286. /* add empty line at end */
  287. if(http10>0) strcat(request,"\r\n");
  288. // printf("Req=%s\n",request);
  289. }
  290. /* vraci system rc error kod */
  291. /**********************
  292. 创建管道和子进程,对http请求进行测试
  293. **********************/
  294. static int bench(void)
  295. {
  296. int i,j,k;
  297. pid_t pid=0;
  298. FILE *f;
  299. /* check avaibility of target server */
  300. i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
  301. if(i<0) {
  302. fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
  303. return 1;
  304. }
  305. close(i);
  306. /* create pipe */
  307. if(pipe(mypipe))
  308. {
  309. perror("pipe failed.");
  310. return 3;
  311. }
  312. /* not needed, since we have alarm() in childrens */
  313. /* wait 4 next system clock tick */
  314. /*
  315. cas=time(NULL);
  316. while(time(NULL)==cas)
  317. sched_yield();
  318. */
  319. /* fork childs */
  320. for(i=0;i<clients;i++)
  321. {
  322. pid=fork();
  323. if(pid <= (pid_t) 0)
  324. {
  325. /* child process or error*/
  326. sleep(1); /* make childs faster */
  327. break;
  328. }
  329. }
  330. if( pid< (pid_t) 0)
  331. {
  332. fprintf(stderr,"problems forking worker no. %d\n",i);
  333. perror("fork failed.");
  334. return 3;
  335. }
  336. if(pid== (pid_t) 0)
  337. {
  338. /* I am a child */
  339. if(proxyhost==NULL)//是否使用proxyhost
  340. benchcore(host,proxyport,request);
  341. else
  342. benchcore(proxyhost,proxyport,request);
  343. /* write results to pipe */
  344. f=fdopen(mypipe[1],"w");
  345. if(f==NULL)
  346. {
  347. perror("open pipe for writing failed.");
  348. return 3;
  349. }
  350. /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
  351. fprintf(f,"%d %d %d\n",speed,failed,bytes);//把每个子进程运行的结果放到管道。
  352. fclose(f);
  353. return 0;
  354. } else
  355. {
  356. f=fdopen(mypipe[0],"r");
  357. if(f==NULL)
  358. {
  359. perror("open pipe for reading failed.");
  360. return 3;
  361. }
  362. setvbuf(f,NULL,_IONBF,0);
  363. speed=0;
  364. failed=0;
  365. bytes=0;
  366. while(1)//父进程读取管道数据,并做加法
  367. {
  368. pid=fscanf(f,"%d %d %d",&i,&j,&k);
  369. if(pid<2)
  370. {
  371. fprintf(stderr,"Some of our childrens died.\n");
  372. break;
  373. }
  374. speed+=i;
  375. failed+=j;
  376. bytes+=k;
  377. /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
  378. if(--clients==0) break;
  379. }
  380. fclose(f);
  381. //输出测试结果
  382. printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
  383. (int)((speed+failed)/(benchtime/60.0f)),
  384. (int)(bytes/(float)benchtime),
  385. speed,
  386. failed);
  387. }
  388. return i;
  389. }
  390. /******************************
  391. 这里才是测试http的地方
  392. @host:地址
  393. @port:端口
  394. @req:http格式方法
  395. ********************************/
  396. void benchcore(const char *host,const int port,const char *req)
  397. {
  398. int rlen;
  399. char buf[1500];
  400. int s,i;
  401. struct sigaction sa;
  402. /* setup alarm signal handler */
  403. sa.sa_handler=alarm_handler;//定时器方法
  404. sa.sa_flags=0;
  405. if(sigaction(SIGALRM,&sa,NULL))
  406. exit(3);
  407. alarm(benchtime);
  408. rlen=strlen(req);
  409. nexttry:while(1)
  410. {
  411. if(timerexpired)//定时器到时后,会设定timerexpired=1,函数就会返回
  412. {
  413. if(failed>0)
  414. {
  415. /* fprintf(stderr,"Correcting failed by signal\n"); */
  416. failed--;
  417. }
  418. return;
  419. }
  420. s=Socket(host,port);      //创建连接
  421. if(s<0) { failed++;continue;} //连接失败,failed加1
  422. if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}//发送失败
  423. if(http10==0)
  424. if(shutdown(s,1)) { failed++;close(s);continue;}
  425. if(force==0) //force等于0表示要读取http请求数据
  426. {
  427. /* read all available data from socket */
  428. while(1)
  429. {
  430. if(timerexpired) break;
  431. i=read(s,buf,1500);
  432. /* fprintf(stderr,"%d\n",i); */
  433. if(i<0)
  434. {
  435. failed++;
  436. close(s);
  437. goto nexttry;
  438. }
  439. else
  440. if(i==0) break;
  441. else
  442. bytes+=i;//读取字节数增加
  443. }
  444. }
  445. if(close(s)) {failed++;continue;}
  446. speed++;//http测试成功一次,speed加1
  447. }
  448. }