美文网首页
源码阅读-webbench_1.5

源码阅读-webbench_1.5

作者: EleZtian | 来源:发表于2017-06-10 13:29 被阅读0次

    Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能,最多可以模拟3万个并发连接去测试网站的负载能力。Webbench使用C语言编写, 代码实在太简洁,源码加起来不到600行。

    阅读项目github下载

    注释代码如下:

    webbench.c
    
    #include "socket.c"
    #include <unistd.h>
    #include <sys/param.h>
    #include <rpc/types.h>
    #include <getopt.h>
    #include <strings.h>
    #include <time.h>
    #include <signal.h>
    
    /* values */
    volatile int timerexpired=0;
    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 */
    
    /* 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;
    int clients=1;
    int force=0;
    int force_reload=0;
    int proxyport=80;
    char *proxyhost=NULL;
    int benchtime=30;
    
    /* internal */
    int mypipe[2];
    char host[MAXHOSTNAMELEN];
    #define REQUEST_SIZE 2048
    char request[REQUEST_SIZE];
    
    /* 选项结构定义 
     * staruct option:
     *  no_argument : 没有参数quir
     *  required    : 需要参数
     *  {“长选项”, 是否有参数, 参数, 短选项}
     * */
    static const struct option long_options[]=
    {
        {"force",no_argument,&force,1},
        {"reload",no_argument,&force_reload,1},
        {"time",required_argument,NULL,'t'},
        {"help",no_argument,NULL,'?'},
        {"http09",no_argument,NULL,'9'},
        {"http10",no_argument,NULL,'1'},
        {"http11",no_argument,NULL,'2'},
        {"get",no_argument,&method,METHOD_GET},
        {"head",no_argument,&method,METHOD_HEAD},
        {"options",no_argument,&method,METHOD_OPTIONS},
        {"trace",no_argument,&method,METHOD_TRACE},
        {"version",no_argument,NULL,'V'},
        {"proxy",required_argument,NULL,'p'},
        {"clients",required_argument,NULL,'c'},
        {NULL,0,NULL,0}
    };
    
    /* prototypes */
    static void benchcore(const char* host,const int port, const char *request);
    static int bench(void);
    static void build_request(const char *url);
    
    /* 闹钟中断函数 */
    static void alarm_handler(int signal)
    {
        timerexpired=1;
    }   
    
    static void usage(void)
    {
        fprintf(stderr,
                    "webbench [option]... URL\n"
                    "  -f|--force               Don't wait for reply from server.\n"
                    "  -r|--reload              Send reload request - Pragma: no-cache.\n"
                    "  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
                    "  -p|--proxy <server:port> Use proxy server for request.\n"
                    "  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
                    "  -9|--http09              Use HTTP/0.9 style requests.\n"
                    "  -1|--http10              Use HTTP/1.0 protocol.\n"
                    "  -2|--http11              Use HTTP/1.1 protocol.\n"
                    "  --get                    Use GET request method.\n"
                    "  --head                   Use HEAD request method.\n"
                    "  --options                Use OPTIONS request method.\n"
                    "  --trace                  Use TRACE request method.\n"
                    "  -?|-h|--help             This information.\n"
                    "  -V|--version             Display program version.\n"
               );
    };
    int main(int argc, char *argv[])
    {
        int opt=0;
        int options_index=0;
        char *tmp=NULL;
    
        if(argc==1)
        {
            usage();
            return 2;
        } 
        /* 解析命令 
         * 需要参数的短选项后面需要:*/
        while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )
        {
            switch(opt)
            {
                case  0 : break;
                case 'f': force=1;break;
                case 'r': force_reload=1;break; 
                case '9': http10=0;break;
                case '1': http10=1;break;
                case '2': http10=2;break;
                case 'V': printf(PROGRAM_VERSION"\n");exit(0);
                case 't': benchtime=atoi(optarg);break; /* optarg 选项参数指针 */      
                case 'p': 
                          /* proxy server parsing server:port */
                          tmp=strrchr(optarg,':');
                          proxyhost=optarg;
                          if(tmp==NULL)
                          {
                              break;
                          }
                          if(tmp==optarg)
                          {
                              fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg);
                              return 2;
                          }
                          if(tmp==optarg+strlen(optarg)-1)
                          {
                              fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg);
                              return 2;
                          }
                          *tmp='\0';
                          proxyport=atoi(tmp+1);break;
                case ':':
                case 'h':
                case '?': usage();return 2;break;
                case 'c': clients=atoi(optarg);break;
            }
        }
        /* optind 当前opt解析位置 
         * if optind == argc 表示解析完了*/
        if(optind==argc) {
            fprintf(stderr,"webbench: Missing URL!\n");
            usage();
            return 2;
        }
    
        if(clients==0) clients=1;
        if(benchtime==0) benchtime=60;
        /* Copyright */
        fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n"
                    "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n"
               );
        /* argv[optind] 表示url */
        build_request(argv[optind]);
        /* print bench info */
        printf("\nBenchmarking: ");
        switch(method)
        {
            case METHOD_GET:
            default:
                printf("GET");break;
            case METHOD_OPTIONS:
                printf("OPTIONS");break;
            case METHOD_HEAD:
                printf("HEAD");break;
            case METHOD_TRACE:
                printf("TRACE");break;
        }
        printf(" %s",argv[optind]);
        switch(http10)
        {
            case 0: printf(" (using HTTP/0.9)");break;
            case 2: printf(" (using HTTP/1.1)");break;
        }
        printf("\n");
        if(clients==1) printf("1 client");
        else
          printf("%d clients",clients);
    
        printf(", running %d sec", benchtime);
        if(force) printf(", early socket close");
        if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport);
        if(force_reload) printf(", forcing reload");
        printf(".\n");
    
        /*开始测试 */
        return bench();
    }
    
    void build_request(const char *url)
    {
        char tmp[10];
        int i;
    
        bzero(host,MAXHOSTNAMELEN);
        bzero(request,REQUEST_SIZE);
    
        if(force_reload && proxyhost!=NULL && http10<1) http10=1;
        if(method==METHOD_HEAD && http10<1) http10=1;
        if(method==METHOD_OPTIONS && http10<2) http10=2;
        if(method==METHOD_TRACE && http10<2) http10=2;
    
        /* 构建headers 头 */
        switch(method)
        {
            default:
            case METHOD_GET: strcpy(request,"GET");break;
            case METHOD_HEAD: strcpy(request,"HEAD");break;
            case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
            case METHOD_TRACE: strcpy(request,"TRACE");break;
        }
    
        strcat(request," ");
        /* 检测URL格式合法性 */
        if(NULL==strstr(url,"://"))
        {
            fprintf(stderr, "\n%s: is not a valid URL.\n",url);
            exit(2);
        }
        if(strlen(url)>1500)
        {
            fprintf(stderr,"URL is too long.\n");
            exit(2);
        }
        if(proxyhost==NULL)
          if (0!=strncasecmp("http://",url,7)) 
          {
              fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
              exit(2);
          }
        /* protocol/host delimiter */
        /* url 开始位置 http为7*/
        i=strstr(url,"://")-url+3;
        /* printf("%d\n",i); */
        
        /* 如果url中没有字符/ */
        if(strchr(url+i,'/')==NULL) {
            fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
            exit(2);
        }
        /* 如果没有代理 */
        if(proxyhost==NULL)
        {
            /* get port from hostname */
            if(index(url+i,':')!=NULL &&
                        index(url+i,':')<index(url+i,'/'))
            {
                /* 获取Host 长度:strchr(url+i, ':') - (url+i) */
                strncpy(host,url+i,strchr(url+i,':')-url-i);
                bzero(tmp,10);
                /* 获取port */
                strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
                /* printf("tmp=%s\n",tmp); */
                proxyport=atoi(tmp);
                if(proxyport==0) proxyport=80;
            } else
            {
                strncpy(host,url+i,strcspn(url+i,"/"));
            }
             printf("Host=%s\n",host);
            strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
        } else /* 使用代理 */
        {
            printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
            /*将url放到request里面*/
            strcat(request,url);
            printf("request:%s\n", request);
        }
        /* 设置http协议版本 */
        if(http10==1)
          strcat(request," HTTP/1.0");
        else if (http10==2)
          strcat(request," HTTP/1.1");
        strcat(request,"\r\n");
        if(http10>0)
          strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");
        if(proxyhost==NULL && http10>0)
        {
            strcat(request,"Host: ");
            strcat(request,host);
            strcat(request,"\r\n");
        }
        if(force_reload && proxyhost!=NULL)
        {
            strcat(request,"Pragma: no-cache\r\n");
        }
        if(http10>1)
          strcat(request,"Connection: close\r\n");
        /* add empty line at end */
        /* 添加一空行 */
        if(http10>0) strcat(request,"\r\n"); 
        // printf("Req=%s\n",request);
    }
    
    /* vraci system rc error kod */
    static int bench(void)
    {
        int i,j,k;    
        pid_t pid=0;
        FILE *f;
    
        /* check avaibility of target server */
        /* 测试是否可以连接这个url */
        i=Socket(proxyhost==NULL?host:proxyhost,proxyport);
        if(i<0) { 
            fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
            return 1;
        }
        /* 关闭连接 */
        close(i);
        /* create pipe 创建管道 
         * 将文件描述符由mypipe[]数组返回
         * mypipe[0] 为管道读取端
         * mypipe[1] 为管道写入端
         * 成功返回0 否则返回-1*/
        if(pipe(mypipe))
        {
            perror("pipe failed.");
            return 3;
        }
    
        /* not needed, since we have alarm() in childrens */
        /* wait 4 next system clock tick */
        /*
           cas=time(NULL);
           while(time(NULL)==cas)
           sched_yield();
           */
    
        /* fork childs */
        for(i=0;i<clients;i++)
        {
            /* 复制一个进程 */
            pid=fork();
            /* 进程创建失败 */
            if(pid <= (pid_t) 0)
            {
                /* child process or error*/
                sleep(1); /* make childs faster */
                break;
            }
        }
    
        if( pid< (pid_t) 0)
        {
            fprintf(stderr,"problems forking worker no. %d\n",i);
            perror("fork failed.");
            return 3;
        }
        /* 子进程 */
        if(pid== (pid_t) 0)
        {
            /* I am a child */
            /* 连接 */
            if(proxyhost==NULL)
              benchcore(host,proxyport,request);
            else
              benchcore(proxyhost,proxyport,request);
    
            /* write results to pipe */
            f=fdopen(mypipe[1],"w");
            if(f==NULL)
            {
                perror("open pipe for writing failed.");
                return 3;
            }
            /* fprintf(stderr,"Child - %d %d\n",speed,failed); */
            fprintf(f,"%d %d %d\n",speed,failed,bytes);
            fclose(f);
            return 0;
        } else /* 父进程 */
        {
            /* 读取结果 */
            f=fdopen(mypipe[0],"r");
            if(f==NULL) 
            {
                perror("open pipe for reading failed.");
                return 3;
            }
            /* 设置文件流缓冲区 缓冲类型为__IONBF, 0字节*/
            /* 清空缓冲区及各项数据 */
            setvbuf(f,NULL,_IONBF,0);
            speed=0;
            failed=0;
            bytes=0;
            
            /* 循环读取 */
            while(1)
            {
                pid=fscanf(f,"%d %d %d",&i,&j,&k);
                if(pid<2)
                {
                    fprintf(stderr,"Some of our childrens died.\n");
                    break;
                }
                speed+=i;
                failed+=j;
                bytes+=k;
                /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */
                if(--clients==0) break;
            }
            fclose(f);
            /* 所有任务完成输出结果 */
            printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
                        (int)((speed+failed)/(benchtime/60.0f)),
                        (int)(bytes/(float)benchtime),
                        speed,
                        failed);
        }
        return i;
    }
    
    void benchcore(const char *host,const int port,const char *req)
    {
        int rlen;
        char buf[1500];
        int s,i;
        /* Linux中信号机制 */
        struct sigaction sa;
    
        /* setup alarm signal handler */
        /* 配置中断函数 */
        sa.sa_handler=alarm_handler;
        sa.sa_flags=0;
        if(sigaction(SIGALRM,&sa,NULL))
          exit(3);
        alarm(benchtime);
    
        rlen=strlen(req);
        nexttry:
            while(1)
            {
                if(timerexpired)/* 闹钟时间到 */
                {
                    if(failed>0)
                    {
                        /* fprintf(stderr,"Correcting failed by signal\n"); */
                        failed--;
                    }
                    return;
                }
                /* 开始连接 */
                s=Socket(host,port);                          
                if(s<0) { failed++;continue;}
                /* 发送header */
                if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}
                if(http10==0)/* 关闭连接 HTTP0.9*/ 
                  if(shutdown(s,1)) { failed++;close(s);continue;}
                if(force==0) 
                {
                    /* read all available data from socket */
                    while(1)
                    {
                        if(timerexpired) break; 
                        i=read(s,buf,1500);
                        /* fprintf(stderr,"%d\n",i); */
                        if(i<0) 
                        { 
                            failed++;
                            close(s);
                            goto nexttry;
                        }
                        else
                          if(i==0) break;
                          else
                            bytes+=i;
                    }
                }
                if(close(s)) {failed++;continue;}
                /* 循环次数 */
                speed++;
            }
    

    相关文章

      网友评论

          本文标题:源码阅读-webbench_1.5

          本文链接:https://www.haomeiwen.com/subject/jagqqxtx.html