xf_said

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

 

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

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

其源代码有2个文件组成

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

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

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

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

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

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

socket.c源代码及注释:

 

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

 

 

wenbench.c源代码及注释

 

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

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

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

其源代码有2个文件组成

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

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

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

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

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

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

socket.c源代码及注释:

 

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

 

 

wenbench.c源代码及注释

 

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