Chinaunix首页 | 论坛 | 博客
  • 博客访问: 6276207
  • 博文数量: 2759
  • 博客积分: 1021
  • 博客等级: 中士
  • 技术积分: 4091
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-11 14:14
文章分类

全部博文(2759)

文章存档

2019年(1)

2017年(84)

2016年(196)

2015年(204)

2014年(636)

2013年(1176)

2012年(463)

分类: C/C++

2015-01-23 10:56:30

原文地址:webbench源码浅析 作者:silence0607

webbench作为一个简单的网站压力测试工具,小巧而简单,其源码仅仅500行左右,是一个学习linux下C编程的好例子。
下载webbench:
,最近更新时间是04年的6月25号!!!
解压后的webbench由下面几个文件组成:

其中有2个C源文件,一个是socket.c 另一个是webbench.c
socket.c
  1. 内部仅包含一个Socket函数,如下:
  2. int Socket(const char *host, int clientPort)
  3. {
  4.     //以host和clientPort构成一对TCP的套接字(服务器)
  5.     //创建失败返回-1  成功返回一个sockt描述符
  6. }
webbench.c
  1. 在webbench.c源文件中,包含了下面几个函数:
  2. static void alarm_handler(int signal)
  3. static void usage(void)
  4. void build_request(const char *url)
  5. static int bench(void)
  6. void benchcore(const char *host, const int port, const char *req)
  7. 当然还有main函数  int main(int argc, char *argv[])
现在用一张图来描述一下整个webbench的工作流程:

  1. 这里先对全局变量做一下说明
  2. /* values */
    volatile int timerexpired=0;     //根据命令行参数-t指定的测试时间判断是否超时
    int speed=0;                     //子进程成功得到服务器响应的总数
    int failed=0;                    //子进程请求失败总数
    int bytes=0;                     //读取到的字节数
    /* globals */
    int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */         //HTTP协议版本定义
    /* Allow: GET, HEAD, OPTIONS, TRACE */
    #define METHOD_GET 0
    #define METHOD_HEAD 1
    #define METHOD_OPTIONS 2
    #define METHOD_TRACE 3
    #define PROGRAM_VERSION "1.5"
    int method=METHOD_GET;           //定义HTTP请求方法GET 此外还支持OPTIONS、HEAD、TRACE方法,在main函数中用switch判断
    int clients=1;                   //默认并发数为1,也就是子进程个数 可以由命令行参数-c指定
    int force=0;                     //是否等待从服务器获取数去数据 0为获取
    int force_reload=0;              //是否使用cache  0为使用
    int proxyport=80;                //代理服务器端口 80
    char *proxyhost=NULL;            //代理服务器IP 默认为NULL
    int benchtime=30;                //测试时间  默认为30秒  可由命令行参数-t指定
    /* internal */
    int mypipe[2];                   //创建管道(半双工) 父子进程间通信,读取/写入数据
    char host[MAXHOSTNAMELEN];       //定义服务器IP
    #define REQUEST_SIZE 2048    
    char request[REQUEST_SIZE];      //HTTP请求信息
由于整个webbench工具核心代码由bench和benchcore两个函数组成,下面仅分析下这两个函数的源码:
  1. /* vraci system rc error kod */
  2. static int bench(void)
  3. {
  4. int i,j,k;
  5. pid_t pid=0;
  6. FILE *f;
  7. /* check avaibility of target server */
  8. i=Socket(proxyhost==NULL?host:proxyhost,proxyport);   //判断是否使用代理服务器,将Socket函数的返回值进行判断
  9. if(i<0) {            //错误处理
  10.  fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
     return 1;
  11. }
  12. close(i);
  13. /* create pipe */
  14. if(pipe(mypipe))     //创建管道及错误处理
  15. {
  16. perror("pipe failed.");
  17. return 3;
  18. }
  19. /* not needed, since we have alarm() in childrens */
  20. /* wait 4 next system clock tick */
  21. /*
  22. cas=time(NULL);
  23. while(time(NULL)==cas)
  24. sched_yield();
  25. */
  26. /* fork childs */
  27. for(i=0;i
  28. {
  29. pid=fork();
  30. if(pid <= (pid_t) 0)
  31. {
  32. /* child process or error*/
  33. sleep(1); /* make childs faster */
  34. break;
  35. }
  36. }
  37. if( pid< (pid_t) 0)        //错误处理,fork调用失败返回负数
  38. {
  39. fprintf(stderr,"problems forking worker no. %d\n",i);
  40. perror("fork failed.");
  41. return 3;
  42. }
  43. if(pid== (pid_t) 0)        //判断pid是否为0,为0则进入子进程执行相应的代码
  44. {
  45. /* I am a child */
  46. if(proxyhost==NULL)        //判断代理,进入benchcore函数
  47. benchcore(host,proxyport,request);
  48. else
  49. benchcore(proxyhost,proxyport,request);
  50. /* write results to pipe */
  51. f=fdopen(mypipe[1],"w");   //打开管道,子进程往管道写数据
  52. if(f==NULL)                //错误处理
  53. {
  54. perror("open pipe for writing failed.");
  55. return 3;
  56. }
  57. /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
  58. fprintf(f,"%d %d %d\n",speed,failed,bytes);    //往管道写数据
  59. fclose(f);
  60. return 0;
  61. } else                     //判断pid是否大于0,大于0进入父进程,执行相应代码
  62. {
  63. f=fdopen(mypipe[0],"r");   //打开管道,父进程从管道读数据,一个管道只能进行半双工的工作(一端读,一端写)
  64. if(f==NULL)
  65. {
  66. perror("open pipe for reading failed.");
  67. return 3;
  68. }
  69. setvbuf(f,NULL,_IONBF,0);
  70. speed=0;
  71. failed=0;
  72. bytes=0;
  73. while(1)
  74. {
  75. pid=fscanf(f,"%d %d %d",&i,&j,&k);      //从管道读数据
  76. if(pid<2)
  77. {
  78. fprintf(stderr,"Some of our childrens died.\n");
  79. break;
  80. }
  81. speed+=i;                  //全局计数器  speed failed bytes
  82. failed+=j;
  83. bytes+=k;
  84. /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
  85. if(--clients==0) break;    //子进程为0,数据读完后,退出循环
  86. }
  87. fclose(f);
  //打印结果
  1. printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
  2. (int)((speed+failed)/(benchtime/60.0f)),
  3. (int)(bytes/(float)benchtime),
  4. speed,
  5. failed);
  6. }
  7. return i;
  8. }
  1. void benchcore(const char *host,const int port,const char *req)
  2. {
  3. int rlen;
  4. char buf[1500];
  5. int s,i;
  6. struct sigaction sa;
  7. /* setup alarm signal handler */
  8. sa.sa_handler=alarm_handler;      //加载信号处理函数
  9. sa.sa_flags=0;
  10. if(sigaction(SIGALRM,&sa,NULL))
  11. exit(3);
  12. alarm(benchtime);    //计时开始
  13. rlen=strlen(req);
  14. nexttry:while(1)     //带go-to语句的while循环
  15. {
  16. if(timerexpired)     //超时则退出函数
  17. {
  18. if(failed>0)
  19. {
  20. /* fprintf(stderr,"Correcting failed by signal\n"); */
  21. failed--;
  22. }
  23. return;
  24. }
  25. s=Socket(host,port);  //建立socket连接,获取socket描述符
  26. if(s<0) { failed++;continue;}  //连接建立失败,failed++
  27. if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}   //往服务器发送请求;如果请求失败,failed++,关闭当前子进程socket描述符,go-to到while循环再来一次
  28. if(http10==0)         //针对HTTP0.9的处理办法
  29. if(shutdown(s,1)) { failed++;close(s);continue;}
  30. if(force==0)          //判断是否从服务器读取数据
  31. {
  32. /* read all available data from socket */
  33. while(1)
  34. {
  35. if(timerexpired) break; //判断超时
  36. i=read(s,buf,1500);      //从服务器读取数据,保存到buff数组中
  37. /* fprintf(stderr,"%d\n",i); */
  38. if(i<0)      //读取数据失败的处理,返回while循环开始处,重新来一次
  39. {
  40. failed++;
  41. close(s);
  42. goto nexttry;
  43. }
  44. else
  45. if(i==0) break;   //读取成功后,bytes统计数据大小
  46. else
  47. bytes+=i;
  48. }
  49. }
  50. if(close(s)) {failed++;continue;}
  51. speed++;
  52. }
  53. }
由于本人对Linux下的C编程仅仅停留在粗略理解《linux C编程实战》一书的基础上,本文如有错误,还请指正
阅读(600) | 评论(0) | 转发(0) |
0

上一篇:linux多进程编程

下一篇:程序优化方法

给主人留下些什么吧!~~