精华内容
下载资源
问答
  • webbench源码

    2017-12-31 16:33:14
    webbench是一款轻量级的性能压测工具,通过剖析源码可以使你更能对其有更深 的理解
  • webbench源码分析

    2016-11-09 19:16:15
    webbench源码分析

    webbench源码分析

    /*
     * (C) Radim Kolar 1997-2004
     * This is free software, see GNU Public License version 2 for
     * details.
     *
     * Simple forking WWW Server benchmark:
     *
     * Usage:
     *   webbench --help
     *
     * Return codes:
     *    0 - sucess
     *    1 - benchmark failed (server is not on-line)
     *    2 - bad param
     *    3 - internal error, fork failed
     * 
     */ 
    #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];
    
    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;           
       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;
      }
     }
     //检查是否提供带请求的url
     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"
             );
    //构建请求对象,严格遵守HTTP协议
     build_request(argv[optind]);
     /* print bench info */
     printf("\nBenchmarking: ");
    
     //选择请求方法,默认是GET
     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;
    //初始化host、request变量(清零)。
      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;
    //构建请求行 如:GET
      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;
      }
     //请求行中添加空格 如:GET {空格}                 
      strcat(request," ");
    
      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 */
      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 */
       //获取待请求的hostname和port
       if(index(url+i,':')!=NULL &&
          index(url+i,':')<index(url+i,'/'))
       {
               strncpy(host,url+i,strchr(url+i,':')-url-i);
               bzero(tmp,10);
               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);
       //构建请求行,填充uri 如:GET /xx/yy/zz
       strcat(request+strlen(request),url+i+strcspn(url+i,"/"));
      } else
      {
       // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);
       strcat(request,url);
      }
      //构建请求行,填充请求的协议:如:GET /xx/yy/zz HTTP/1.0 
      if(http10==1)
              strcat(request," HTTP/1.0");
      else if (http10==2)
              strcat(request," HTTP/1.1");
      //换行
      /*
      *GET /xx/yy/zz HTTP/1.0
      *
      */
      strcat(request,"\r\n");
      //构建请求头:如:
      /*
      *GET /xx/yy/zz HTTP/1.0
      *User-Agent: WebBench "PROGRAM_VERSION"
      *Host: {hostname}
      *Connection: close
      *
      */
      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 */
      //检查服务端
      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 */
      //创建管道
      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 */
      //fork出进程
      for(i=0;i<clients;i++)
      {
               pid=fork();
               if(pid <= (pid_t) 0)
               {
               //子进程或者是出错
                       /* child process or error*/
                       sleep(1); /* make childs faster */
                       break;
               }
      }
    //fork子进程出错
      if( pid< (pid_t) 0)
      {
              fprintf(stderr,"problems forking worker no. %d\n",i);
              perror("fork failed.");
              return 3;
      }
    //pid=0 当前在子进程中
      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;
              }
              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;
     struct sigaction sa;
    
     /* setup alarm signal handler */
     //设置alarm信号的处理方法
     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;
        }
        //建立与服务端的socket的连接
        s=Socket(host,port);                          
        if(s<0) { failed++;continue;} 
        if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}
        if(http10==0) 
                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++;
     }
    }
    
    
    /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
     *
     * This module has been modified by Radim Kolar for OS/2 emx
     */
    
    /***********************************************************************
      module:       socket.c
      program:      popclient
      SCCS ID:      @(#)socket.c    1.5  4/1/94
      programmer:   Virginia Tech Computing Center
      compiler:     DEC RISC C compiler (Ultrix 4.1)
      environment:  DEC Ultrix 4.3 
      description:  UNIX sockets code.
     ***********************************************************************/
    
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <fcntl.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    #include <sys/time.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    
    int Socket(const char *host, int clientPort)
    {
        int sock;
        unsigned long inaddr;
        struct sockaddr_in ad;
        struct hostent *hp;
        //初始化ad变量
        memset(&ad, 0, sizeof(ad));
        ad.sin_family = AF_INET;
    
        inaddr = inet_addr(host);
        if (inaddr != INADDR_NONE)
            memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
        else
        {
            hp = gethostbyname(host);
            if (hp == NULL)
                return -1;
            memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
        }
        ad.sin_port = htons(clientPort);
        //创建客户端socket
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
            return sock;
        //连接服务端
        if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
            return -1;
        //返回已建立连接的socket
        return sock;
    }
    展开全文
  • webbench源码阅读

    2016-12-02 16:12:39
    webbench源码阅读webbench 是个压力测试工具它采用的是fork+请求socket的方式,实现压力测试。1 他通过拼接http请求,然后计算时间, 2 fork多个子进程,读取http数据写入管道 3 父进程读取管道,统计次数

    webbench源码阅读

    webbench 是个压力测试工具

    它采用的是fork+请求socket的方式,实现压力测试。

    1 他通过拼接http请求,然后计算时间,
    2 fork多个子进程,读取http数据写入管道
    3 父进程读取管道,统计次数
    
    展开全文
  • Webbench源码剖析

    2016-08-31 10:36:55
    Webbench源码剖析Web Bench是一个网站压力测试的工具。其最后更新时间是2004年,已经十年多了。其源代码总共才不到600行,全部使用C语言编写,最多可以模拟3万个并发连接。真可谓是简洁代码的代表之作。

    Webbench源码剖析

    目录

    Web Bench是一个网站压力测试的工具。其最后更新时间是2004年,已经十年多了。其源代码总共才不到600行,全部使用C语言编写,最多可以模拟3万个并发连接。真可谓是简洁代码的代表之作。
    用法:
    可以在安装后直接输入 webbench 或 webbench -h 或 webbench –help. 可以看到:

    webbench [option]... URL  
      -f|--force               Don't wait for reply from server.  
      -r|--reload              Send reload request - Pragma: no-cache.  
      -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.  
      -p|--proxy <server:port> Use proxy server for request.  
      -c|--clients <n>         Run <n> HTTP clients at once. Default one.  
      -9|--http09              Use HTTP/0.9 style requests.  
      -1|--http10              Use HTTP/1.0 protocol.  
      -2|--http11              Use HTTP/1.1 protocol.  
      --get                    Use GET request method.  
      --head                   Use HEAD request method.  
      --options                Use OPTIONS request method.  
      --trace                  Use TRACE request method.  
      -?|-h|--help             This information.  
      -V|--version             Display program version.  

    说一下主要的几个选项: 指定 -f 时不等待服务器数据返回, -t 为指定压力测试运行时间, -c 指定由多少个客户端发起测试请求。
    -9 -1 -2 分别为指定 HTTP/0.9 HTTP/1.0 HTTP/1.1。

    Webbench的代码实现原理也是相当简单,就是一个父进程fork出很多个子进程,子进程分别去执行http测试,最后把执行结果汇总写入管道,父进程读取管道数据然后进行最终计算。整个工具的实现流程如下:
    这里写图片描述

    其源代码包括两个文件,非常简单,一个是socket.c,一个是webbench.c。其中,socket.c是用来建立socket连接的,webbench.c负责实现主要的功能。

    socket.c源代码及注释:

    [cpp] 
    /* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ 
     * 
     * This module has been modified by Radim Kolar for OS/2 emx 
     */  
    
    /*********************************************************************** 
      module:       socket.c 
      program:      popclient 
      SCCS ID:      @(#)socket.c    1.5  4/1/94 
      programmer:   Virginia Tech Computing Center 
      compiler:     DEC RISC C compiler (Ultrix 4.1) 
      environment:  DEC Ultrix 4.3  
      description:  UNIX sockets code. 
     ***********************************************************************/  
    
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <fcntl.h>  
    #include <netinet/in.h>  
    #include <arpa/inet.h>  
    #include <netdb.h>  
    #include <sys/time.h>  
    #include <string.h>  
    #include <unistd.h>  
    #include <stdio.h>  
    #include <stdlib.h>  
    #include <stdarg.h>  
    /*********** 
    功能:通过地址和端口建立网络连接 
    @host:网络地址 
    @clientPort:端口 
    Return:建立的socket连接。 
            如果返回-1,表示建立连接失败 
    ************/  
    int Socket(const char *host, int clientPort)  
    {  
        int sock;  
        unsigned long inaddr;  
        struct sockaddr_in ad;  
        struct hostent *hp;  
    
        memset(&ad, 0, sizeof(ad));  
        ad.sin_family = AF_INET;  
    
        inaddr = inet_addr(host);//将点分的十进制的IP转为无符号长整形  
        if (inaddr != INADDR_NONE)  
            memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));  
        else//如果host是域名  
        {  
            hp = gethostbyname(host);//用域名获取IP  
            if (hp == NULL)  
                return -1;  
            memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);  
        }  
        ad.sin_port = htons(clientPort);  
    
        sock = socket(AF_INET, SOCK_STREAM, 0);  
        if (sock < 0)  
            return sock;  
            //连接  
        if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)  
            return -1;  
        return sock;  
    }  

    socket函数的大致内容如下:

    int Socket(const char *host, int clientPort)
    {
        //以host为服务器端ip,clientPort为服务器端口号建立socket连接
        //连接类型为TCP,使用IPv4网域
        //一旦出错,返回-1
        //正常连接,则返回socket描述符
    }

    Webbench.c中包含以下几个主要的函数:
    - static void usage(void):提示Webbench的用法及命令
    - static void alarm_handler(int signal) :信号处理函数,时钟结束时进行调用
    - void build_request(const char *url):创建http连接请求
    - static int bench(void):创建管道和子进程,调用测试http函数,实现父子进程通信并计算结果
    - void benchcore(const char *host,const int port,const char *req):对http请求进行测试(子进程的具体工作)

    主要流程:

      1. 解析命令行参数,根据命令行指定参数设定变量,可以认为是初始化配置。
      2.根据指定的配置构造 HTTP 请求报文格式。
      3.开始执行 bench 函数,先进行一次 socket 连接建立与断开,测试是否可以正常访问。
      4.建立管道,派生根据指定进程数派生子进程。
      5.每个子进程调用 benchcore 函数,先通过 sigaction 安装信号,用 alarm 设置闹钟函数,到时间后会产生SIGALRM信号,调用信号处理函数使子进程停止。接着不断建立 socket 进行通信,与服务器交互数据,直到收到信号结束访问测试。子进程将访问测试结果写进管道。
      6.父进程读取管道数据,汇总子进程信息,收到所有子进程消息后,输出汇总信息,结束。
    

    程序调用的流程示意图

    全局变量:

    volatile int timerexpired=0;//判断压测时长是否已经到达设定的时间
    int speed=0; //记录进程成功得到服务器响应的数量
    int failed=0;//记录失败的数量(speed表示成功数,failed表示失败数)
    int bytes=0;//记录进程成功读取的字节数
    int http10=1;//http版本,0表示http0.9,1表示http1.0,2表示http1.1
    int method=METHOD_GET; //默认请求方式为GET,也支持HEAD、OPTIONS、TRACE
    int clients=1;//并发数目,默认只有1个进程发请求,通过-c参数设置
    int force=0;//是否需要等待读取从server返回的数据,0表示要等待读取
    int force_reload=0;//是否使用缓存,1表示不缓存,0表示可以缓存页面
    int proxyport=80; //代理服务器的端口
    char *proxyhost=NULL; //代理服务器的ip
    int benchtime=30; //压测时间,默认30秒,通过-t参数设置
    int mypipe[2]; //使用管道进行父进程和子进程的通信
    char host[MAXHOSTNAMELEN]; //服务器端ip
    char request[REQUEST_SIZE]; //所要发送的http请求
    

    每个函数的具体分析:
    (1)alarm_handler();

    static void alarm_handler(int signal)
    {
       timerexpired=1;
    }

    webbench在运行时可以设定压测的持续时间,以秒为单位。例如我们希望测试30秒,也就意味着压测30秒后程序应该退出了。webbench中使用信号(signal)来控制程序结束。函数alarm_handler()是在到达结束时间时运行的信号处理函数。它仅仅是将一个记录是否超时的变量timerexpired标记为true。后面会看到,在程序的while循环中会不断检测此值,只有timerexpired=1,程序才会跳出while循环并返回。

    (2)static void usage(void);

    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"
        );
    };

    其中,-9 -1 -2 分别代表http0.9、http1.0和http1.1协议。webbench支持GET,HEAD,OPTIONS,TRACE四种请求方式。

    (3)void build_request(const char *url);
    这个函数主要操作全局变量char request[REQUEST_SIZE],根据url填充其内容。一个典型的http GET请求如下:

    GET /test.jpg HTTP/1.1
    User-Agent: WebBench 1.5
    Host:192.168.10.1
    Pragma: no-cache
    Connection: close

    build_request函数的目的就是要把类似于以上这一大坨信息全部存到全局变量request[REQUEST_SIZE]中,其中换行操作使用的是”\r\n”。而以上这一大坨信息的具体内容是要根据命令行输入的参数,以及url来确定的。该函数使用了大量的字符串操作函数,例如strcpy,strstr,strncasecmp,strlen,strchr,index,strncpy,strcat。

    (4)主函数main();

    int main(int argc, char *argv[])
    {
        /*函数最开始,使用getopt_long函数读取命令行参数,来设置全局变量的值。
        关于getopt_long的具体使用方法,这里有一个配有讲解的小例子,可以帮助学习:
        http://blog.csdn.net/lanyan822/article/details/7692013
        在此期间如果出现错误,会调用函数(1)告知用户此工具使用方法,然后退出。
        */
    
        build_request(argv[optind]); //参数读完后,argv[optind]即放在命令行最后的url
                                  //调用函数(2)建立完整的HTTP request,
                                //HTTP request存储在全部变量char request[REQUEST_SIZE]
    
        /*接下来的部分,main函数的所有代码都是在网屏幕上打印此次测试的信息,
        例如即将测试多少秒,几个并发进程,使用哪个HTTP版本等。
        这些信息并非程序核心代码,因此我们也略去。
        */
    
        return bench(); //简简单单一句话,原来,压力测试在这最后一句才真正开始!
                     //所有的压测都在bench函数(即函数(5))实现
    }

    (5)static int bench(void);

    static int bench(void){
      int i,j,k;    
      pid_t pid=0;
      FILE *f;
    
      i=Socket(proxyhost==NULL?host:proxyhost,proxyport); //调用了Socket.c文件中的函数
      if(i<0){ /*错误处理*/ }
      close(i);
    
      if(pipe(mypipe)){ /*错误处理*/ } //管道用于子进程向父进程回报数据
      for(i=0;i<clients;i++){//根据clients大小fork出来足够的子进程进行测试
           pid=fork();
           if(pid <= (pid_t) 0){
               sleep(1); /* make childs faster */
               break;
           }
      }
      if( pid< (pid_t) 0){ /*错误处理*/ }
    
      if(pid== (pid_t) 0){//如果是子进程,调用benchcore进行测试
        if(proxyhost==NULL)
          benchcore(host,proxyport,request);
        else
          benchcore(proxyhost,proxyport,request);
    
         f=fdopen(mypipe[1],"w");//子进程将测试结果输出到管道
         if(f==NULL){ /*错误处理*/ }
         fprintf(f,"%d %d %d\n",speed,failed,bytes);
         fclose(f);
         return 0;
      } else{//如果是父进程,则从管道读取子进程输出,并作汇总
         f=fdopen(mypipe[0],"r");
          if(f==NULL) { /*错误处理*/ }
          setvbuf(f,NULL,_IONBF,0);
          speed=0;  failed=0;  bytes=0;
    
          while(1){ //从管道读取数据,fscanf为阻塞式函数
              pid=fscanf(f,"%d %d %d",&i,&j,&k);
              if(pid<2){ /*错误处理*/ }
              speed+=i;  failed+=j;  bytes+=k;
              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;
    }

    函数先进行了一次socket连接,确认能连通以后,才进行后续步骤。调用pipe函数初始化一个管道,用于子进行向父进程汇报测试数据。子进程根据clients数量fork出来。每个子进程都调用函数(6)进行测试,并将结果输出到管道,供父进程读取。父进程负责收集所有子进程的测试数据,并汇总输出。

    (6)void benchcore(const char *host,const int port,const char *req);

    void benchcore(const char *host,const int port,const char *req){
     int rlen;
     char buf[1500];//记录服务器响应请求所返回的数据
     int s,i;
     struct sigaction sa;
    
     sa.sa_handler=alarm_handler; //设置函数1为信号处理函数
     sa.sa_flags=0;
     if(sigaction(SIGALRM,&sa,NULL)) //超时会产生信号SIGALRM,用sa中的指定函数处理
        exit(3); 
    
     alarm(benchtime);//开始计时
     rlen=strlen(req);
     nexttry:while(1){
        if(timerexpired){//一旦超时则返回
           if(failed>0){failed--;}
           return;
        }
        s=Socket(host,port);//调用Socket函数建立TCP连接
        if(s<0) { failed++;continue;} 
        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){//全局变量force表示是否要等待服务器返回的数据
            while(1){
            if(timerexpired) break;
              i=read(s,buf,1500);//从socket读取返回数据
              if(i<0) { 
              failed++;
              close(s);
              goto nexttry;
            }else{
              if(i==0) break;
                else
                  bytes+=i;
            }
            }
        }
        if(close(s)) {failed++;continue;}
        speed++;
     }
    }

    benchcore是子进程进行压力测试的函数,被每个子进程调用。这里使用了SIGALRM信号来控制时间,alarm函数设置了多少时间之后产生SIGALRM信号,一旦产生此信号,将运行函数(1),使得timerexpired=1,这样可以通过判断timerexpired值来退出程序。另外,全局变量force表示我们是否在发出请求后需要等待服务器的响应结果。

    疑问及解答

    1. 线程占用的空间比进程要小,而且线程切换的时间开销也小,但为什么程序的实现上采用的是fork进程而不是使用多线程呢?
      答:因为默认情况下:
      主线程+辅助线程<253个自己的线程<=256
      含主线程和一个辅助线程,最多255个,即一个用户只能生成253个线程。
      而进程的最大数目则跟系统本身限制相关。
      2.webbench中在多个进程进行写管道的情况下,在代码中没有采取同步措施,程序是如何保持数据正确呢?
      答:管道写的数据不大于 PIPE_BUF 时,系统可以保证写的原子性。在2.6.29内核中,\include\linux\limits.h定义:
    #define PIPE_BUF 4096

    涉及到的知识点有:
    命令行参数解析(getopt_long)、 信号处理(sigaction)、 socket 管道读写 。

    源码中的一些函数用法及启示

    getoptlong()

    在写程序时常常需要对命令行参数进行处理,当命令行参数个数较多时,如果按照顺序一个一个定义参数含义很容易造成混乱,而且如果程序只按顺序处理参数的话,一些“可选参数”的功能将很难实现。
    在Linux中,可以使用getopt、getopt_long、getopt_long_only来对处理这个问题。
    具体用法可见http://blog.csdn.net/cashey1991/article/details/7942809

    sleep(1)的功能:
    让CPU能够空闲下来,不至于使CPU使用率一直高居不下;本线程放弃cpu时间片,其他线程处理之后,再开始本线程,多线程处理socket接收发送的时候经常这样处理,防止接收发送出现问题。


    展开全文
  • Webbench源码分析

    千次阅读 2016-06-07 22:44:38
    Webbench源码分析 简介Webbench是一个在Linux下使用的非常简单的网站侧压工具。它使用fork()模拟多个客户端同时访问url,测试网站在压力下工作的性能。 工作原理主函数进行必要的准备工作,进入bench开始压测 bench...

    Webbench源码分析

    • 简介

      Webbench是一个在Linux下使用的非常简单的网站侧压工具。它使用fork()模拟多个客户端同时访问url,测试网站在压力下工作的性能。

    • 工作原理

      • 主函数进行必要的准备工作,进入bench开始压测
      • bench函数使用fork模拟出多个客户端,调用socket并发请求,每个子进程记录自己的访问数据,并切入管道
      • 父进程从管道读取子进程的输出信息
      • 使用alarm函数进行时间控制,到时间后会差生SIGALRM信号,调用信号处理函数使子进程停止
      • 最后只留下父进程将所有子进程的输出数据汇总计算,输出到屏幕上。
    • **其工作流程如下图:

    • 源码分析

      Webbench源码中主要包括两个源文件,一个是socket.c和webbench.c两个文件。socket.c主要是封装的一个socket模块,webbench.c是主要文件,完成网站测压的整个过程。webbench.c源码的阅读主要从mian,其整体流程为:main——>对命令行进行参数解析——>调用build_request函数构建HTTP的“Get”请求头——>调用bench测试函数(其中子进程调用benchcore函数进行压力测试),之后主进程从管道读取消息,并输出到标准输出上即可。

      下面是对socket.c函数的解析:

      // socket描述符,主要以host和clientPort构成一对TCP的套接字(host支持域名),创建失败返回-1,成功返回一个
      int Socket(const char *host, int clientPort)
      {
          int sock;
          unsigned long inaddr;
          struct sockaddr_in ad;
          struct hostent *hp;
      
          memset(&ad, 0, sizeof(ad));
          ad.sin_family = AF_INET;
      
          // 若字符串有效,则将字符串转换为32位二进制。网络字节序的IPV4地址,否则为INADDR_NONe
          inaddr = inet_addr(host);
          if (inaddr != INADDR_NONE)
              memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
          else
          {
              // 返回对应于给定主机名的包含主机名字和地址信息的hostent结构指针
              hp = gethostbyname(host);
              if (hp == NULL)
                  return -1;
              memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
          }
          // 将一个无符号短整型的主机数值转换为网络字节顺序
          ad.sin_port = htons(clientPort);
      
          // 创建socket套接字
          sock = socket(AF_INET, SOCK_STREAM, 0);
          if (sock < 0)
              return sock;
          // 连接到相应的主机
          if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)
              return -1;
          return sock;
      }
      

      Webbench.c源码分析

      首先是main函数,mian函数首先进行参数解析,然后执行build_request函数,构建HTTP请求头,最后调用核心函数bench()执行网站测压测试。

      // 主函数
      int main(int argc, char *argv[])
      {
       // getopt_long的返回字符
       int opt=0;
       // getopt_long的第五个参数,一般为0
       int options_index=0;
       char *tmp=NULL;
      
       if(argc==1)
       {
            usage();
          return 2;
       }
       // 使用getopt_long函数读取命令行参数,来设置所涉及到的全局变量的值。
       // getopt_long函数支持长选项的命令行解析,其声明如下:
       // int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *long_options, int *longindex)
       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);
         // -t 后跟压力测试时间,optarg返回,使用atoi转换成整数
         case 't': benchtime=atoi(optarg);break;
         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; // 并发数目 -c N
        }
       }
      
       // 扫描参数选项时,optind标识下一个选项的索引;扫描结束后,标识第一个非选项参数索引;如
       // 果optind=argc,说明非选项参数即服务器URL缺失。此变量是系统定义的。
       // optind返回第一个不包含选项的命令名参数,此处为URL值
       if(optind==argc)
       {
          fprintf(stderr,"webbench: Missing URL!\n");
          usage();
          return 2;
        }
       // 此处多做一次判断,可预防BUG,因为上文并发数目用户可能写0
       if(clients==0) clients=1;
       // 压力测试时间默认为30s,如果用户写成0,则默认为60s
       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"
           );
       // 调用build_request函数构建完整的HTTP请求头,HTTP request存储在全局变量char request[REQUEST_SIZE]
       build_request(argv[optind]);   // 参数为URL值
       /* print bench info */
       // 在屏幕上打印测试的信息,如HTTP协议,请求方式,并发个数,请求时间等
       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");
       // 调用bench函数,开始压力测试,bench()为压力测试核心代码
       return bench();
      }
      

      下面是build_request函数分析

      // 此函数主要目的是要把类似于http GET请求的信息全部存储到全局变量request[REQUEST_SIZE]
      // 中,其中换行操作使用"\r\n"。其中应用了大量的字符串操作函数。
      // 创建url请求连接,HTTP头,创建好的请求放在全局变量request中
      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;
      
        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," ");
      
        if(NULL==strstr(url,"://"))  // strstr(str1, str2)用于判断str2是否是str1的子串
        {
            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)
           // 未使用代理服务器的情况下,只允许HTTP协议
             if (0!=strncasecmp("http://",url,7))    // 比较前7个字符串
             { fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
                   exit(2);
                 }
        /* protocol/host delimiter */
        // 指向"://"后的第一个字母
        i=strstr(url,"://")-url+3;
        /* printf("%d\n",i); */
        // URL后必须的'/'
        if(strchr(url+i,'/')==NULL)   //url + i 指向http://后第一个位置
        {
            fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n");
            exit(2);
        }
        // 如果未使用代理服务器,就表示肯定是HTTP协议
        if(proxyhost==NULL)
        {
         /* get port from hostname */
         // 如果是server:port形式,解析主机和端口
         if(index(url+i,':')!=NULL &&
            index(url+i,':')<index(url+i,'/'))             // 判断url中是否指定了端口号
         {
             strncpy(host,url+i,strchr(url+i,':')-url-i);    // 取出主机地址
             bzero(tmp,10);
             strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);
             /* printf("tmp=%s\n",tmp); */
           // 目标端口
             proxyport=atoi(tmp);    // 端口号转换为int
             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);
         // 如若使用代理服务器
         strcat(request,url);
        }
        if(http10==1)
            strcat(request," HTTP/1.0");
        else if (http10==2)
            strcat(request," HTTP/1.1");
          // 完成如 GET/HTTP1.1后,添加"\r\n"
        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");
        }
        // force_reload=1和存在代理服务器,则不缓存
        if(force_reload && proxyhost!=NULL)
        {
            strcat(request,"Pragma: no-cache\r\n");
        }
        // 如果为HTTP1.1,则存在长连接,应将Connection置位close
        if(http10>1)
            strcat(request,"Connection: close\r\n");
        /* add empty line at end */
        // 最后不要忘记在请求后添加“\r\n”
        if(http10>0) strcat(request,"\r\n");
        // printf("Req=%s\n",request);
      }
      

      下面是bench函数解析,此函数开始先进行一次socket连接,确认能连接以后,才进行后续步骤;调用pipe函数初始化一个管道,用于子进程想父进程汇总测试数据。而子进程是主进程通过fork函数复制出来的;之后每隔子进程都调用benchcore函数进行测试,并将结果输出到管道,供父进程读取。父进程负责收集所有子进程的测试数据,并进行汇总输出显示即可。

      static int bench(void)
      {
        int i,j,k;
        pid_t pid=0;
        FILE *f;
      
        /* check avaibility of target server */
        // 进行socket连接,调用了Socket.c文件中的函数,主要是为了测试远程主机是否能够连通
        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 */
        // 创建管道,管道用于子进程想父进程汇报数据
        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 */
        // 根据clients大小fork出来足够的子进程进行测试
        for(i=0;i<clients;i++)
        {
             pid=fork();
             if(pid <= (pid_t) 0)   // pid=0 ->子进程.pid < 0 -> error
             {
                 /* child process or error*/
                 // 注意这里子进程sleep(1)
                 sleep(1); /* make childs faster */
                 break;   // 子进程跳出循环orfork出错父进程跳出循环
             }
        }
      
        if( pid< (pid_t) 0)    // fork出错
        {
            // 错误处理
            fprintf(stderr,"problems forking worker no. %d\n",i);
            perror("fork failed.");
            return 3;
        }
      
        // 如果是子进程,调用benchcore进行测试
        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); */
          // 子进程将speed failed bytes写进管道
          fprintf(f,"%d %d %d\n",speed,failed,bytes);
          fclose(f);
          // 子进程完成任务,返回退出
          return 0;
        }
        else
        {
            // 父进程从管道读取子进程输出,并做汇总,然后输出显示
            f=fdopen(mypipe[0],"r");   // mypipe[0]与标准流相结合
            // 错误处理
            if(f==NULL)
            {
                perror("open pipe for reading failed.");
                return 3;
            }
            // _IONBF(无缓冲):直接从流中读入数据或直接向流中写入数据,而没有缓冲区
            setvbuf(f,NULL,_IONBF,0);    // 设置无缓冲区
            // 虽然子进程不能污染父进程的这几个变量,但用前重置一下,在这里是个好习惯
            speed=0;
            failed=0;
            bytes=0;
      
            // 从管道读取数据,fscanf为阻塞式函数
            // 从管道中读取每个子进程的任务执行请求,并计数
            while(1)
            {
                // 通过f从管道读取数据,注意fscanf为阻塞式函数
                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;
      }
      

      由于bench函数子进程调用了benchcore函数,而benchcore函数是测试函数,它通过使用SIGALARM信息来控制时间,alarm函数设置了多少时间之后产生SIGALRM信号,一旦产生此信息,将运行alam_handler函数,是的timerexpired=1,这样之后可以通过判断timerexpired值来退出程序。此外,全局变量force表示是否发出请求后需要等待服务器的相应结果。

      // benchcore函数是子进程进行压力测试的函数,被每个子进程调用。其函数中参数信息如下:
      // host:地址
      // port:端口
      // req:http格式方法
      void benchcore(const char *host,const int port,const char *req)
      {
       int rlen;
       // 记录服务器相应请求所返回的数据
       char buf[1500];
       int s,i;
       struct sigaction sa;
      
       /* setup alarm signal handler */
       // 当程序执行到指定的秒数之后,发送SIGALRM信号,即设置alam_handler函数为信号处理函数
       sa.sa_handler=alarm_handler;
       sa.sa_flags=0;
       // sigaction成功则返回0,失败则返回-1,超时会产生信号SIGALRM,用sa指定函数处理
       if(sigaction(SIGALRM,&sa,NULL))
          exit(3);
       // 开始计时
       alarm(benchtime);
      
       rlen=strlen(req);
       // 无限执行请求,直到收到SIGALRM信号将timerexpired设置为1时
       nexttry:while(1)
       {
          // 一旦超时,则返回
          if(timerexpired)
          {
             if(failed>0)
             {
                /* fprintf(stderr,"Correcting failed by signal\n"); */
                failed--;
             }
             return;
          }
          // 连接远程服务器,通过调用Socket函数建立TCP连接
          s=Socket(host,port);
          // 连接失败,failed数加一
          if(s<0) { failed++;continue;}
          // 发出请求,header大小与发送的不相等,则失败
          if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}
          // 针对http0.9做的特殊处理,则关闭socket的写操作,成功返回0,错误返回-1
          if(http10==0)
              if(shutdown(s,1)) { failed++;close(s);continue;}
          // 全局变量force表示是否要等待服务器返回的数据
          // 如果等待数据返回,则读取响应数据,计算传输的字节数
          // 发出请求后需要等待服务器的响应结果 force=0表示等待从Server返回的数据
          if(force==0)
          {
            /* read all available data from socket */
              while(1)
              {
              if(timerexpired) break;   // timerexpired默认为0,在规定时间内读取当为1时表示定时结束
              // 从socket读取返回数据
                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++;
       }
      }
      
    • 参考文献

      http://armsword.com/2014/10/26/webbench-source-analyse/

      http://blog.csdn.net/jiange_zh/article/details/50461790

      http://www.cnblogs.com/xuning/p/3888699.html

    展开全文
  • Webbench源码解析与实践源码下载官网 http://home.tiscali.cz/~cz210552/webbench.html源码运行解压后可以发现代码文件只有socket.c和webbench.c新建xcode工程,添加代码文件编译报错:socket.c重定义解决:删除...
  • 网站压测工具Webbench源码分析 原文链接:点击打开链接 Webbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能。Webbench使用...
  • Webbench源码学习源码解析webbench.c1.main提供程序入口,解析参数进行网络请求相关变量设置。* volatile代码中使用了 volatile 关键字来修饰 int 变量 timerexpired 防止编译器对其进行优化。嵌入式编程中经常用到...
  • Webbench源码下载地址 Webbech能测试处在相同硬件上,不同服务的性能以及不同硬件上同一个服务的运行状况。webBech的标准测试可以向我们展示服务器的 两项 内容:每秒钟相应请求数和每秒钟传输数据量。webbench不但...
  • 前言 Webbench是一个网站压力测试的工具。由Lionbridge公司开发,Webbech的标准测试可以向我们展示服务器的两项内容,分别为每秒钟请求数和每...webbench源码一共包含两个源文件:socket.c和webbench.c 下载地址: ...
  • 网站压测工具Webbench源码分析 原文链接:点击打开链接 Webbench是一个在Linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能。...
  • webbench源码剖析

    2017-08-30 20:39:08
    在http://blog.csdn.net/scmuzi18/article/details/77049710这篇文章中介绍了webbench的安装及使用,于是今天我们将对webbench源码简单的剖析。 webbench中一个开源的测压工具,我们能看到他有两个文件socket.c和...
  • 面对我从来没有见到过的结构体、库函数,我只能一点一点的去网上查,今天就结合我自己看webBench源码的构成来记录下我自己的感受。在后面有什么地方我理解的不到位,希望各位不要吝啬,给予指正。 1、web
  • WebBench源码分析

    2015-05-05 21:52:00
    源码分析共享地址:https://github.com/fivezh/WebBench 下载源码后编译源程序后即可执行: sudo make clean sudo make & make install 1. 使用方法 five@master:~/github/OpenCCode/WebBench$ ./...
  • webbench 源码解析

    2015-06-03 14:56:13
    webbench作为一个简单的网站压力测试工具,小巧简单,其源码仅仅600行左右,是一个学习linux下C编程的好例子。 解压后的webbench由下面几个文件组成: 2个C源文件,一个是socket.c 另一个是webbench.c socket...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 593
精华内容 237
关键字:

webbench源码