1. /****************************************************************************
  2. *
  3. * webbench-1.5_hacking
  4. *
  5. * 1.这是webbench-1.5版本中webbench.c(主程序)的源码,源码不到700行(除去注释).
  6. * 2.通过分析、阅读该源码,可以一窥浏览器访问服务器的原理以及web服务器
  7. * 压力测试的原理.
  8. * 3.知识量:
  9. * 1.C语言;
  10. * 2.Unix或类Unix系统编程;
  11. * 3.微量的http协议(请求行、消息头、实体内容);
  12. * 4.如何阅读别人的代码( 从main函数开始 :) );
  13. * 4.webbench-1.5 文件结构如下:
  14. * .
  15. * |-- COPYRIGHT -> debian/copyright
  16. * |-- ChangeLog -> debian/changelog
  17. * |-- Makefile -------->makefile 文件
  18. * |-- debian
  19. * | |-- changelog
  20. * | |-- control
  21. * | |-- copyright
  22. * | |-- dirs
  23. * | `-- rules
  24. * |-- socket.c -------->里面定义了Socket()函数供webbench.c调用
  25. * |-- webbench.1 -------->说明文档,使用shell命令查看: less webbench.1
  26. * `-- webbench.c -------->你接下来要阅读的文件
  27. *
  28. * 5.如何阅读该文档:
  29. * 1.linux下使用vi/vim配和ctags,windows下使用Source Insight,当然你也
  30. * 可以用其他文本编辑器看.
  31. * 2.先找到main函数,然后就可以开始阅读了,遇到对应的函数,就去看对应的
  32. * 函数.
  33. * 3.对于有些函数,本人没有添加注释,或者说本人觉得没必要.
  34. * 4.祝您好运. :)
  35. *
  36. * 6.webbench-1.5版本下载url: http://home.tiscali.cz/~cz210552/webbench.html
  37. *
  38. * 如果您对本文有任何意见、提议,可以发邮件至zengjf42@163.com,会尽快回复.
  39. * 本文的最终解释权归本人(曾剑锋)所有,仅供学习、讨论.
  40. *
  41. * 2015-3-24 阴 深圳 尚观 Var
  42. *
  43. ***************************************************************************/
  44.  
  45. /*
  46. * (C) Radim Kolar 1997-2004
  47. * This is free software, see GNU Public License version 2 for
  48. * details.
  49. *
  50. * Simple forking WWW Server benchmark:
  51. *
  52. * Usage:
  53. * webbench --help
  54. *
  55. * Return codes:
  56. * 0 - sucess
  57. * 1 - benchmark failed (server is not on-line)
  58. * 2 - bad param
  59. * 3 - internal error, fork failed
  60. *
  61. */
  62.  
  63. #include "socket.c"
  64. /**
  65. * 以下是socket.c中的主要代码:
  66. *
  67. * //
  68. * // Socket函数完成的工作:
  69. * // 1. 转换IP,域名,填充struct sockaddr_in,获取对应的socket描述符;
  70. * // 2. 连接服务器;
  71. * //
  72. *
  73. * int Socket(const char *host, int clientPort)
  74. * {
  75. * //
  76. * // 局部变量说明:
  77. * // 1. sock : 用来保存要返回的socket文件描述符;
  78. * // 2. inaddr : 保存转换为网络序列的二进制数据;
  79. * // 3. ad : 保存连接网络服务器的地址,端口等信息;
  80. * // 4. hp : 指向通过gethostbyname()获取的服务器信息;
  81. * //
  82. *
  83. * int sock;
  84. * unsigned long inaddr;
  85. * struct sockaddr_in ad;
  86. * struct hostent *hp;
  87. *
  88. * memset(&ad, 0, sizeof(ad));
  89. * ad.sin_family = AF_INET;
  90. *
  91. * //
  92. * // 这一段主要完成以下功能:
  93. * // 1. 如果传入的是点分十进制的IP,那么直接转换得到网络字节序列IP;
  94. * // 2. 如果传入的域名,这需要是用gethostbyname()解析域名获取主机IP;
  95. * //
  96. * inaddr = inet_addr(host); //将点分十进制IP转换成网络序列的二进制数据
  97. * //如果host不是点分十进制的,那么返回INADDR_NONE
  98. * if (inaddr != INADDR_NONE)
  99. * memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
  100. * else
  101. * {
  102. * hp = gethostbyname(host);
  103. * if (hp == NULL)
  104. * return -1;
  105. * memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
  106. * }
  107. * ad.sin_port = htons(clientPort);
  108. *
  109. * //
  110. * // 这一段主要完成的工作:
  111. * // 1. 获取socket;
  112. * // 2. 连接网络服务器;
  113. * //
  114. * sock = socket(AF_INET, SOCK_STREAM, 0);
  115. * if (sock < 0)
  116. * return sock;
  117. * if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
  118. * return -1;
  119. * return sock;
  120. * }
  121. *
  122. */
  123. #include <unistd.h>
  124. #include <sys/param.h>
  125. #include <rpc/types.h>
  126. #include <getopt.h>
  127. #include <strings.h>
  128. #include <time.h>
  129. #include <signal.h>
  130.  
  131. /* values */
  132. volatile int timerexpired=; //定时器定时到了的标志,子进程根据这个标志退出
  133. int speed=; //正常响应请求数
  134. int failed=; //不正常响应请求数
  135. int bytes=; //从服务器接收返回的数据字节数
  136.  
  137. /* globals */
  138. /**
  139. * 支持的网络协议,默认是: http/1.0
  140. * 1. 0 - http/0.9
  141. * 2. 1 - http/1.0
  142. * 3. 2 - http/1.1
  143. */
  144. int http10=;
  145.  
  146. /* Allow: GET, HEAD, OPTIONS, TRACE */
  147. #define METHOD_GET 0
  148. #define METHOD_HEAD 1
  149. #define METHOD_OPTIONS 2
  150. #define METHOD_TRACE 3
  151. #define PROGRAM_VERSION "1.5"
  152. int method = METHOD_GET; //默认请求方式get
  153.  
  154. int clients = ; //默认的客户端数量
  155. int force = ; //连接访问web后是否接收服务器返回的数据
  156. int force_reload = ; //是否让浏览器缓存页面
  157. int proxyport = ; //默认代理端口
  158. char *proxyhost = NULL; //代理主机域名
  159. /**
  160. * 默认的测试时间:30s,主要是给子进程的闹钟,时间到了timerexpired会置1,
  161. * 子进程会根据这个条件退出循环,并通过管道给父进程发送benchtime对应的
  162. * 时间内对服务器访问的数据:speed,failed,bytes.
  163. */
  164. int benchtime = ;
  165.  
  166. /* internal */
  167. /**
  168. * 父进程与子进程通信通过管道进行通信:
  169. * 1. mypipe[0] : 是读的管道端口;
  170. * 2. mypipe[1] : 是写的管道端口;
  171. */
  172. int mypipe[];
  173. /**
  174. * 存放点分十进制字符串或者域名
  175. */
  176. char host[MAXHOSTNAMELEN];
  177. /*
  178. * 保存http协议请求头,主要是在build_request()中完成相关操作;
  179. */
  180. #define REQUEST_SIZE 2048
  181. char request[REQUEST_SIZE];
  182.  
  183. /**
  184. * struct option是getopt.h中定义的结构体:
  185. *
  186. * struct option
  187. * {
  188. * const char *name; //表示长参数名
  189. *
  190. * //
  191. * // # define no_argument 0 //表示该参数后面没有参数
  192. * // # define required_argument 1 //表示该参数后面一定要跟个参数
  193. * // # define optional_argument 2 //表示该参数后面可以跟,也可以不跟参数值
  194. * //
  195. * int has_arg;
  196. *
  197. * //
  198. * // 用来决定,getopt_long()的返回值到底是什么:
  199. * // 1. 如果flag是NULL(通常情况),则函数会返回与该项option匹配的val值;
  200. * // 2. 如果flag不是NULL,则将val值赋予flag所指向的内存,并且返回值设置为0;
  201. * //
  202. * int *flag;
  203. * int val; //和flag联合决定返回值
  204. * };
  205. */
  206. static const struct option long_options[]=
  207. {
  208. {"force",no_argument,&force,},
  209. {"reload",no_argument,&force_reload,},
  210. {"time",required_argument,NULL,'t'},
  211. {"help",no_argument,NULL,'?'},
  212. {"http09",no_argument,NULL,''},
  213. {"http10",no_argument,NULL,''},
  214. {"http11",no_argument,NULL,''},
  215. {"get",no_argument,&method,METHOD_GET},
  216. {"head",no_argument,&method,METHOD_HEAD},
  217. {"options",no_argument,&method,METHOD_OPTIONS},
  218. {"trace",no_argument,&method,METHOD_TRACE},
  219. {"version",no_argument,NULL,'V'},
  220. {"proxy",required_argument,NULL,'p'},
  221. {"clients",required_argument,NULL,'c'},
  222. {NULL,,NULL,}
  223. };
  224.  
  225. /* prototypes */
  226. static void benchcore(const char* host,const int port, const char *request);
  227. static int bench(void);
  228. static void build_request(const char *url);
  229.  
  230. /**
  231. * alarm_handler函数功能:
  232. * 1. 闹钟信号处理函数,当时间到了的时候,timerexpired被置1,表示时间到了;
  233. * 2. benchcore()中会根据timerexpired值来判断子进程的运行;
  234. *
  235. */
  236. static void alarm_handler(int signal)
  237. {
  238. timerexpired=;
  239. }
  240.  
  241. /**
  242. * usage函数功能:
  243. * 输出webbench的基本是用方法.
  244. */
  245. static void usage(void)
  246. {
  247. fprintf(stderr,
  248. "webbench [option]... URL\n"
  249. " -f|--force Don't wait for reply from server.\n"
  250. " -r|--reload Send reload request - Pragma: no-cache.\n"
  251. " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n"
  252. " -p|--proxy <server:port> Use proxy server for request.\n"
  253. " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n"
  254. " -9|--http09 Use HTTP/0.9 style requests.\n"
  255. " -1|--http10 Use HTTP/1.0 protocol.\n"
  256. " -2|--http11 Use HTTP/1.1 protocol.\n"
  257. " --get Use GET request method.\n"
  258. " --head Use HEAD request method.\n"
  259. " --options Use OPTIONS request method.\n"
  260. " --trace Use TRACE request method.\n"
  261. " -?|-h|--help This information.\n"
  262. " -V|--version Display program version.\n"
  263. );
  264. };
  265.  
  266. /**
  267. * main函数完成功能:
  268. * 1. 解析命令行参数;
  269. * 2. 组合http请求头;
  270. * 3. 打印一些初步解析出来的基本信息,用于查看对比信息;
  271. * 4. 调用bench创建子进程去访问服务器;
  272. */
  273. int main(int argc, char *argv[])
  274. {
  275. /**
  276. * 局部变量说明:
  277. * 1. opt : 返回的操作符;
  278. * 2. options_index : 当前解析到的长命令行参数的下标;
  279. * 3. tmp : 指向字符的指针;
  280. */
  281. int opt=;
  282. int options_index=;
  283. char *tmp=NULL;
  284.  
  285. if(argc==)
  286. {
  287. usage();
  288. return ;
  289. }
  290.  
  291. while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
  292. {
  293. switch(opt)
  294. {
  295. case : break;
  296. case 'f': force=;break; //不接收服务器返回的数据
  297. case 'r': force_reload=;break; //不缓存请求数据,目前不知有何用
  298. case '': http10=;break; //选择http/0.9
  299. case '': http10=;break; //选择http/1.0
  300. case '': http10=;break; //选择http/1.1
  301. case 'V': printf(PROGRAM_VERSION"\n");exit(); //返回webbench版本号
  302. case 't': benchtime=atoi(optarg);break; //设置基准测试时间
  303. case 'p':
  304. /* proxy server parsing server:port */
  305. /**
  306. * 传入的参数格式:<IP:port>或者<域名:port>,可能的错误有以下3种可能:
  307. * 1. 传入的参数没有是用:分开IP(或域名)和端口号,包括了没有传参数;
  308. * 2. 使用了':'号,但是没有给出IP;
  309. * 3. 使用了':'号,但是没有给出端口号;
  310. */
  311. tmp=strrchr(optarg,':');
  312. proxyhost=optarg;
  313. if(tmp==NULL)
  314. {
  315. break;
  316. }
  317. if(tmp==optarg)
  318. {
  319. fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
  320. return ;
  321. }
  322. if(tmp==optarg+strlen(optarg)-)
  323. {
  324. fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
  325. return ;
  326. }
  327. /**
  328. * 将':'换成'\0',这样就得到了IP(或域名)字符串和port字符串,并通过atoi()获取端口号
  329. */
  330. *tmp='\0';
  331. proxyport=atoi(tmp+);break;
  332. case ':':
  333. case 'h':
  334. case '?': usage();return ;break;
  335. case 'c': clients=atoi(optarg);break; //指定要生成多少个客户端,默认是1个
  336. }
  337. }
  338.  
  339. /**
  340. * 命令行参数没有给出URL
  341. */
  342. if(optind==argc) {
  343. fprintf(stderr,"webbench: Missing URL!\n");
  344. usage();
  345. return ;
  346. }
  347.  
  348. /**
  349. * 修正客户端数量和运行时间
  350. */
  351. if(clients==) clients=;
  352. if(benchtime==) benchtime=;
  353.  
  354. /* Copyright */
  355. fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
  356. "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
  357. );
  358.  
  359. /**
  360. * 创建发送给http服务器的请求头
  361. */
  362. build_request(argv[optind]);
  363.  
  364. /* print bench info */
  365. /**
  366. * 接下来这部分都是打印出初步解析出来的数据,可以用来检查是否符合要求
  367. */
  368. printf("\nBenchmarking: ");
  369. switch(method)
  370. {
  371. case METHOD_GET:
  372. default:
  373. printf("GET");break;
  374. case METHOD_OPTIONS:
  375. printf("OPTIONS");break;
  376. case METHOD_HEAD:
  377. printf("HEAD");break;
  378. case METHOD_TRACE:
  379. printf("TRACE");break;
  380. }
  381. printf(" %s",argv[optind]);
  382. switch(http10)
  383. {
  384. case : printf(" (using HTTP/0.9)");break;
  385. case : printf(" (using HTTP/1.1)");break;
  386. }
  387. printf("\n");
  388. if(clients==)
  389. printf("1 client");
  390. else
  391. printf("%d clients",clients);
  392.  
  393. printf(", running %d sec", benchtime);
  394. if(force)
  395. printf(", early socket close");
  396. if(proxyhost!=NULL)
  397. printf(", via proxy server %s:%d",proxyhost,proxyport);
  398. if(force_reload)
  399. printf(", forcing reload");
  400. printf(".\n");
  401.  
  402. /**
  403. * 调用bench函数,完成相应的功能
  404. */
  405. return bench();
  406. }
  407.  
  408. /**
  409. * build_request函数完成功能:
  410. * 1. 初始化host和request数组;
  411. * 2. 检查给出的url参数是否合法;
  412. * 3. 合成对应http协议的请求头;
  413. */
  414. void build_request(const char *url)
  415. {
  416. /**
  417. * 局部变量说明:
  418. * 1. tmp : 用于存储端口号;
  419. * 2. i : 循环计数;
  420. */
  421. char tmp[];
  422. int i;
  423.  
  424. /**
  425. * 初始化host和request数组,为下面的操作作准备
  426. */
  427. bzero(host,MAXHOSTNAMELEN);
  428. bzero(request,REQUEST_SIZE);
  429.  
  430. /**
  431. * 不同的请求方式,对应不同的协议标准,这里相当于校正请求协议
  432. */
  433. if(force_reload && proxyhost!=NULL && http10<)
  434. http10=;
  435. if(method==METHOD_HEAD && http10<)
  436. http10=;
  437. if(method==METHOD_OPTIONS && http10<)
  438. http10=;
  439. if(method==METHOD_TRACE && http10<)
  440. http10=;
  441.  
  442. switch(method)
  443. {
  444. default:
  445. case METHOD_GET: strcpy(request,"GET");break;
  446. case METHOD_HEAD: strcpy(request,"HEAD");break;
  447. case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
  448. case METHOD_TRACE: strcpy(request,"TRACE");break;
  449. }
  450.  
  451. strcat(request," ");
  452.  
  453. /**
  454. * url中如果不存在"://"说明是非法的URL地址
  455. */
  456. if(NULL==strstr(url,"://"))
  457. {
  458. fprintf(stderr, "\n%s: is not a valid URL.\n",url);
  459. exit();
  460. }
  461.  
  462. /**
  463. * url字符串长度不能长于1500字节
  464. */
  465. if(strlen(url)>)
  466. {
  467. fprintf(stderr,"URL is too long.\n");
  468. exit();
  469. }
  470.  
  471. /**
  472. * 如果没有设置代理服务器,并且协议不是http,说明出错了.
  473. */
  474. if(proxyhost==NULL)
  475. if (!=strncasecmp("http://",url,))
  476. {
  477. fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
  478. exit();
  479. }
  480.  
  481. /* protocol/host delimiter */
  482. /**
  483. * 接下来是解析URL并合成请求行
  484. */
  485.  
  486. i=strstr(url,"://")-url+;
  487. /* printf(" %d\n",i); */ //如果url = "http://www.baidu.com:80/", i = 7
  488. if(strchr(url+i,'/')==NULL)
  489. {
  490. fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
  491. exit();
  492. }
  493.  
  494. /**
  495. * 这段代码主要完成一下内容:
  496. * 1. 如果是通过代理服务器对服务器进行访问,那么proxyhost和proxyport就直接
  497. * 是代理服务器的IP和端口号;
  498. * 2. 如果是直接访问目标服务器,则需要从URL地址中解析出IP(或者域名)和端口号;
  499. */
  500. if(proxyhost==NULL)
  501. {
  502. /* get port from hostname */
  503. if(index(url+i,':')!=NULL &&
  504. index(url+i,':')<index(url+i,'/'))
  505. {
  506. //获取host主机域名
  507. strncpy(host,url+i,strchr(url+i,':')-url-i);
  508.  
  509. //没有给出端口号,就直接是用默认的端口号: 80
  510. bzero(tmp,);
  511. strncpy(tmp,index(url+i,':')+,strchr(url+i,'/')-index(url+i,':')-);
  512. /* printf("tmp=%s\n",tmp); */
  513. proxyport=atoi(tmp);
  514. if(proxyport==) proxyport=;
  515. }
  516. else
  517. {
  518. /**
  519. * 获取host主机域名,没有给出端口号,就直接是用默认的端口号: 80
  520. */
  521. strncpy(host,url+i,strcspn(url+i,"/"));
  522. }
  523. // printf("Host=%s\n",host);
  524. strcat(request+strlen(request),url+i+strcspn(url+i,"/")); //这里是获取URI
  525. } else
  526. {
  527. // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
  528. // 代理服务器需要完整的URL去访问服务器,而不仅仅是URI
  529. strcat(request,url);
  530. }
  531.  
  532. /**
  533. * 当前的使用的http协议的版本
  534. */
  535. if(http10==)
  536. strcat(request," HTTP/1.0");
  537. else if (http10==)
  538. strcat(request," HTTP/1.1");
  539. strcat(request,"\r\n");
  540.  
  541. /**
  542. * 到这里请求行就已经合成完毕了,接下来要合成消息头
  543. */
  544. if(http10>)
  545. strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
  546. if(proxyhost==NULL && http10>)
  547. {
  548. strcat(request,"Host: ");
  549. strcat(request,host);
  550. strcat(request,"\r\n");
  551. }
  552. /**
  553. * 不缓存请求页面,查资料说是对浏览器有效,难道服务器也需要,
  554. * 不知为何这里也需要?
  555. */
  556. if(force_reload && proxyhost!=NULL)
  557. {
  558. strcat(request,"Pragma: no-cache\r\n");
  559. }
  560. /**
  561. * 使用短连接,即服务器返回后就断开连接
  562. */
  563. if(http10>)
  564. strcat(request,"Connection: close\r\n");
  565.  
  566. /* add empty line at end */
  567. /**
  568. * 按不同http协议要求,是否空出一行,下面是请求实体,如果是post请求,
  569. * 需要把请求参数放在这部分.到这里,消息头也就合成完了,接下
  570. * 来就是是用这个请求头去访问http服务器了
  571. */
  572. if(http10>) strcat(request,"\r\n");
  573. // printf("Req=%s\n",request);
  574. }
  575.  
  576. /* vraci system rc error kod */
  577. /**
  578. * bench函数完成以下功能:
  579. * 1. 试探性的尝试一次是否能够正常连接服务器,如果连接失败,也就没必要继续后续处理了;
  580. * 2. 创建管道,用于父子进程通信;
  581. * 3. 创建clients对应数量的子进程;
  582. * 4. 子进程:
  583. * 1. 对服务器进行benchtime秒的连接访问,获取对应的failed,speed,bytes值;
  584. * 2. 当时间到了benchtime秒以后,打开写管道;
  585. * 3. 将子进程自己测试得到的failed,speed,bytes值发送给父进程;
  586. * 4. 关闭写管道文件描述符;
  587. * 5. 父进程:
  588. * 1. 打开读管道;
  589. * 2. 设置管道一些参数,初始化父进程的failed,speed,bytes变量;
  590. * 3. while循环不断去获取子进程传输过来的数据,直到子进程全部退出;
  591. * 4. 关闭读管道;
  592. * 5. 对数据进行处理,并打印输出;
  593. */
  594. static int bench(void)
  595. {
  596. /**
  597. * 局部变量说明:
  598. * 1. i : for循环暂存变量,这里也暂存了一下阿socket文件描述符,
  599. * 还暂存从子进程传给父进程的speed数据;;
  600. * 2. j : 暂存从子进程传给父进程的failed数据;
  601. * 3. k : 暂存从子进程传给父进程的byttes数据;
  602. * 4. pid : fork()出子进程时,保存进程描述符的;
  603. * 5. f : 保存打开的管道的文件指针;
  604. */
  605. int i,j,k;
  606. pid_t pid=;
  607. FILE *f;
  608.  
  609. /* check avaibility of target server */
  610. /**
  611. * 测试一下我们要测试的服务器是否能够正常连接.
  612. * Socket函数完成的工作:
  613. * 1. 转换IP,域名,填充struct sockaddr_in,获取对应的socket描述符;
  614. * 2. 连接服务器;
  615. */
  616. i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
  617. if(i<) {
  618. fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
  619. return ;
  620. }
  621. close(i);
  622.  
  623. /* create pipe */
  624. /**
  625. * 创建管道,主要用于父进程和子进程通信
  626. */
  627. if(pipe(mypipe))
  628. {
  629. perror("pipe failed.");
  630. return ;
  631. }
  632.  
  633. /* not needed, since we have alarm() in childrens */
  634. /* wait 4 next system clock tick */
  635. /*
  636. cas=time(NULL);
  637. while(time(NULL)==cas)
  638. sched_yield();
  639. */
  640.  
  641. /* fork childs */
  642. for(i=;i<clients;i++)
  643. {
  644. pid=fork();
  645. /**
  646. * 子进程获取到的pid=0,所以子进程会理解跳出循环
  647. * 不会再创建子进程,而父进程则会跳过这个判断,继续
  648. * 创建子进程,知道数量达到clients的值.
  649. */
  650. if(pid <= (pid_t) )
  651. {
  652. /* child process or error*/
  653. sleep(); /* make childs faster */
  654. break;
  655. }
  656. }
  657.  
  658. //创建子进程失败
  659. if( pid< (pid_t) )
  660. {
  661. fprintf(stderr,"problems forking worker no. %d\n",i);
  662. perror("fork failed.");
  663. return ;
  664. }
  665.  
  666. /**
  667. * 这一部分完成的工作:
  668. * 1. 子进程:
  669. * 1. 对服务器进行benchtime秒的连接访问,获取对应的failed,speed,bytes值;
  670. * 2. 当时间到了benchtime以后,打开写管道;
  671. * 3. 将子进程自己测试得到的failed,speed,bytes值发送给父进程;
  672. * 4. 关闭写管道文件描述符;
  673. * 2. 父进程:
  674. * 1. 打开读管道;
  675. * 2. 设置管道一些参数,初始化父进程的failed,speed,bytes变量;
  676. * 3. while循环不断去获取子进程传输过来的数据,直到子进程全部退出;
  677. * 4. 关闭读管道;
  678. * 5. 对数据进行处理,并打印输出;
  679. */
  680.  
  681. if(pid== (pid_t) )
  682. {
  683. /* I am a child */
  684. if(proxyhost==NULL)
  685. benchcore(host,proxyport,request);
  686. else
  687. benchcore(proxyhost,proxyport,request);
  688.  
  689. /* write results to pipe */
  690. f=fdopen(mypipe[],"w");
  691. if(f==NULL)
  692. {
  693. perror("open pipe for writing failed.");
  694. return ;
  695. }
  696. /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
  697. fprintf(f,"%d %d %d\n",speed,failed,bytes);
  698. fclose(f);
  699. return ;
  700. }
  701. else
  702. {
  703. f=fdopen(mypipe[],"r");
  704. if(f==NULL)
  705. {
  706. perror("open pipe for reading failed.");
  707. return ;
  708. }
  709. setvbuf(f,NULL,_IONBF,); //设置管道为无缓冲类型
  710. speed=;
  711. failed=;
  712. bytes=;
  713.  
  714. while()
  715. {
  716. pid=fscanf(f,"%d %d %d",&i,&j,&k);
  717. if(pid<)
  718. {
  719. fprintf(stderr,"Some of our childrens died.\n");
  720. break;
  721. }
  722. speed+=i;
  723. failed+=j;
  724. bytes+=k;
  725. /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
  726. if(--clients==) break;
  727. }
  728. fclose(f);
  729.  
  730. printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
  731. (int)((speed+failed)/(benchtime/60.0f)),
  732. (int)(bytes/(float)benchtime),
  733. speed,
  734. failed);
  735. }
  736. return i;
  737. }
  738.  
  739. /**
  740. * benchcore函数完成功能:
  741. * 1. 注册闹钟处理函数,设置闹钟时间,具体时间由benchtime给出,默认是30s;
  742. * 2. while循环里判断闹钟时间是否到了,如果到了退出循环;
  743. * 3. while循环里连接服务器;
  744. * 4. while循环里发送http请求头给服务器;
  745. * 5. while循环里判断是否需要接受服务器数据;
  746. * 6. 关闭与服务器的连接;
  747. * 7. 在整个过程中,以下变量会统计在benchtime给出的时间内的一些信息:
  748. * 1. failed : 连接服务器失败和传输数据过程中失败的连接数;
  749. * 2. speed : 正常连接服务器,并且正常传输数据的连接数
  750. * 3. bytes : 从服务器获取到的字节数;
  751. */
  752. void benchcore(const char *host,const int port,const char *req)
  753. {
  754. /**
  755. * 局部变量说明:
  756. * 1. rlen : 请求字符串的长度;
  757. * 2. buf : 保存从服务器获取的数据;
  758. * 3. s : socket文件描述符;
  759. * 4. i : 保存从服务器读到的字节数;
  760. * 5. sa : 信号结构体变量;
  761. */
  762. int rlen;
  763. char buf[];
  764. int s,i;
  765. struct sigaction sa;
  766.  
  767. /* setup alarm signal handler */
  768. /**
  769. * 注册闹钟信号处理函数,并设置闹钟时间为benchtime,默认是30s;
  770. */
  771. sa.sa_handler=alarm_handler;
  772. sa.sa_flags=;
  773. if(sigaction(SIGALRM,&sa,NULL))
  774. exit();
  775. alarm(benchtime);
  776.  
  777. rlen=strlen(req);
  778. nexttry:
  779. while()
  780. {
  781. if(timerexpired) //检查闹钟时间是否到了,如果到了,子进程就退出
  782. {
  783. if(failed>)
  784. {
  785. /* fprintf(stderr,"Correcting failed by signal\n"); */
  786. failed--;
  787. }
  788. return;
  789. }
  790.  
  791. s=Socket(host,port);
  792. if(s<)
  793. {
  794. failed++;
  795. continue;
  796. }
  797.  
  798. /**
  799. * 将请求头发给web服务器
  800. */
  801. if(rlen!=write(s,req,rlen))
  802. {
  803. /**
  804. * 写数据失败,代表当前连接有问题,也就是失败了
  805. */
  806. failed++;
  807. close(s);
  808. continue;
  809. }
  810.  
  811. if(http10==) // http/0.9协议
  812. if(shutdown(s,))
  813. {
  814. failed++;
  815. close(s);
  816. continue;
  817. }
  818.  
  819. if(force==) //是否读取服务器返回数据
  820. {
  821. /* read all available data from socket */
  822. while()
  823. {
  824. if(timerexpired) break; //判断是否已经闹钟到时
  825. i=read(s,buf,);
  826. /* fprintf(stderr,"%d\n",i); */
  827. /**
  828. * 对当前次连接数据读取错误,那么重来
  829. */
  830. if(i<)
  831. {
  832. failed++;
  833. close(s);
  834. goto nexttry;
  835. }
  836. else
  837. if(i==)
  838. break;
  839. else
  840. bytes+=i; //统计一共读取了多少字节
  841. }
  842. }
  843.  
  844. /**
  845. * 关闭socket文件,如果出错,那么增加失败的统计数据
  846. */
  847. if(close(s))
  848. {
  849. failed++;
  850. continue;
  851. }
  852. /**
  853. * 成功完成连接,数据传输,获取等等工作,speed统计数据+1
  854. */
  855. speed++;
  856. }
  857. }

webbench-1.5_hacking的更多相关文章

  1. Webbench性能测试

    1.下载安装:立即下载  官网:http://home.tiscali.cz/~cz210552/webbench.html 2.解压缩:tar -zxvf webbench-1.5.tar.gz 3 ...

  2. webbench之使用(二)

    [root@lam7 ~]# webbench -helpwebbench [option]... URL -f|--force                Don't wait for reply ...

  3. webbench之编译安装(一)

    1.编译安装:   1 2 3 4 [root@hexuweb102 ~]$wget http://blog.s135.com/soft/linux/webbench/webbench-1.5.tar ...

  4. Linux下四款Web服务器压力测试工具(http_load、webbench、ab、siege)介绍

    一.http_load程序非常小,解压后也不到100Khttp_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般不会把 ...

  5. linux下如何安装webbench

    1.上传webbench,解压 2.make&&make install进行安装 如果报没有man1目录,则要用 mkdir -p /usr/local/man/man1 然后 mak ...

  6. 网站压力测试工具webbench使用说明

    一.webbench简介        Webbench是有名的网站压力测试工具,它是由Lionbridge公司(http://www.lionbridge.com)开发.它的帮助文件和文档请到:ww ...

  7. WebBench源码分析与心得

    源码:https://github.com/EZLippi/WebBench   关键全局变量: speed 成功次数 failed 失败次数 bytes 接收字节数 benchtime 执行时长(秒 ...

  8. webbench 压力测试

    原文 webbench最多可以模拟3万个并发连接去测试网站的负载能力,个人感觉要比Apache自带的ab压力测试工具好用,安装使用也特别方便,并且非常小. 主要是 -t 参数用着比较爽,下面参考了张宴 ...

  9. 压力测试 webbench

    Linux下 webbench最多可以模拟3万个并发连接去测试网站的负载能力 webbench -c -t http://127.0.0.1/phpinfo.php 说明: -c 客户端数量(并发数量 ...

随机推荐

  1. jekins 实现Django项目的自动部署(ubuntu16.04,python2.7,django1.11)

    1.依赖的插件 如果插件下载失败 更换源 http://mirror.xmission.com/jenkins/updates/current/update-center.json 因为我们只需要构建 ...

  2. 【转】VMware网络连接模式—桥接、NAT以及仅主机模式的详细介绍和区别

    ☞ 本文主要介绍软件『VMware Workstation(虚拟机)』的相关内容:VMware网络连接模式—桥接.NAT以及仅主机模式的详细介绍和区别. 其下列版本/分支可以参考本文: 全部版本/分支 ...

  3. 装B必备之 快捷键配置

    作为一个程序员 所有程序都用快捷来这是装B必备的无形装B 最为致命.... 开始搞起 第一步配置环境变量 在系统D盘新建一个文件夹  D:\cache; 然后把这个路径 配置上 D:\cache; 最 ...

  4. zlib__ZC

    官网:http://www.zlib.net/ ,所有版本下载:http://www.zlib.net/fossils/ ZC: 我下载的是 zlib-1.2.3.tar.gz 和 zlib-1.2. ...

  5. SpringBoot中的数据库连接池

    内置的连接池 目前Spring Boot中默认支持的连接池有dbcp,dbcp2, tomcat, hikari三种连接池. 数据库连接可以使用DataSource池进行自动配置. 由于Tomcat数 ...

  6. 各种容器与服务器的区别与联系 Servlet容器 WEB容器 Java EE容器 应用服务器 WEB服务器 Java EE服务器

    转自:https://blog.csdn.net/tjiyu/article/details/53148174 各种容器与服务器的区别与联系 Servlet容器 WEB容器 Java EE容器 应用服 ...

  7. 秒杀多线程第六篇 经典线程同步 事件Event

    原文地址:http://blog.csdn.net/morewindows/article/details/7445233 上一篇中使用关键段来解决经典的多线程同步互斥问题,由于关键段的“线程所有权” ...

  8. 最齐全的Android studio 快捷键(亲测可用)

    Action Mac OSX Win/Linux 注释代码(//) Cmd + / Ctrl + / 注释代码(/**/) Cmd + Option + / Ctrl + Alt + / 格式化代码 ...

  9. 使用poi导出Excel,并设定单元格内容类型,抛出异常

    本例子使用的是HSSF,为Excel2003提供处理方案. 设定为输入类型为数值 import org.apache.poi.hssf.usermodel.DVConstraint; import o ...

  10. JavaWeb重定向和转发

    if (user != null && passWord.equals(user.getPassWord())) { // 登录成功 // response.sendRedirect( ...