Web Bench 1.5介绍 html
1.用GDB单步走的,走了父进程也走了子进程。父进程负责从管道里读取数据,每一个子进程通过socket,connect,write,read,负责Get请求写数据到管道。子进程至关于socket网络编程里的client客户端。web
2.GDB调试时,经过设置set follow-fork-mode [parent][child]分别执行父子进程。编程
3.调试时输入指令为:-c 10 -t 5 http://www.baidu.com/数组
4.代码很少可是牵扯的东西仍是很多的。建议本身跟一边。服务器
1 /* 2 * Return codes: 3 * 0 - sucess 4 * 1 - benchmark failed (server is not on-line) 5 * 2 - bad param 6 * 3 - internal error, fork failed 7 */ 8 #include "socket.c" 9 #include <unistd.h> 10 #include <sys/param.h> 11 #include <rpc/types.h> //pid_t 12 #include <getopt.h> 13 #include <strings.h> 14 #include <time.h> 15 #include <signal.h> 16 17 /* values */ 18 volatile int timerexpired=0; //volatile 在多进程中,变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以避免出错 http://www.cnblogs.com/chio/archive/2007/11/24/970632.html 19 int speed=0; 20 int failed=0; 21 int bytes=0; 22 /* globals */ 23 int http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */ 24 /* Allow: GET, HEAD, OPTIONS, TRACE */ 25 #define METHOD_GET 0 26 #define METHOD_HEAD 1 27 #define METHOD_OPTIONS 2 28 #define METHOD_TRACE 3 29 #define PROGRAM_VERSION "1.5" 30 int method=METHOD_GET; 31 int clients=1; 32 int force=0; 33 int force_reload=0; 34 int proxyport=80; 35 char *proxyhost=NULL; 36 int benchtime=30; 37 /* internal */ 38 int mypipe[2]; 39 char host[MAXHOSTNAMELEN]; 40 #define REQUEST_SIZE 2048 41 char request[REQUEST_SIZE]; 42 43 static const struct option long_options[]= 44 { 45 {"force",no_argument,&force,1}, 46 {"reload",no_argument,&force_reload,1}, 47 {"time",required_argument,NULL,'t'}, 48 {"help",no_argument,NULL,'?'}, 49 {"http09",no_argument,NULL,'9'}, 50 {"http10",no_argument,NULL,'1'}, 51 {"http11",no_argument,NULL,'2'}, 52 {"get",no_argument,&method,METHOD_GET}, 53 {"head",no_argument,&method,METHOD_HEAD}, 54 {"options",no_argument,&method,METHOD_OPTIONS}, 55 {"trace",no_argument,&method,METHOD_TRACE}, 56 {"version",no_argument,NULL,'V'}, 57 {"proxy",required_argument,NULL,'p'}, 58 {"clients",required_argument,NULL,'c'}, 59 {NULL,0,NULL,0} 60 }; 61 62 /* prototypes */ 63 static void benchcore(const char* host,const int port, const char *request); 64 static int bench(void); 65 static void build_request(const char *url); 66 67 static void alarm_handler(int signal) 68 { 69 timerexpired=1; 70 } 71 72 static void usage(void) 73 { 74 fprintf(stderr, 75 "webbench [option]... URL\n" 76 " -f|--force Don't wait for reply from server.\n" 77 " -r|--reload Send reload request - Pragma: no-cache.\n" 78 " -t|--time <sec> Run benchmark for <sec> seconds. Default 30.\n" 79 " -p|--proxy <server:port> Use proxy server for request.\n" 80 " -c|--clients <n> Run <n> HTTP clients at once. Default one.\n" 81 " -9|--http09 Use HTTP/0.9 style requests.\n" 82 " -1|--http10 Use HTTP/1.0 protocol.\n" 83 " -2|--http11 Use HTTP/1.1 protocol.\n" 84 " --get Use GET request method.\n" 85 " --head Use HEAD request method.\n" 86 " --options Use OPTIONS request method.\n" 87 " --trace Use TRACE request method.\n" 88 " -?|-h|--help This information.\n" 89 " -V|--version Display program version.\n" 90 ); 91 }; 92 int main(int argc, char *argv[]) 93 { 94 int opt=0; 95 int options_index=0; 96 char *tmp=NULL; 97 98 if(argc==1) 99 { 100 usage(); 101 return 2; 102 } 103 104 while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF ) 105 { 106 switch(opt) 107 { 108 case 0 : break; 109 case 'f': force=1;break; 110 case 'r': force_reload=1;break; 111 case '9': http10=0;break; 112 case '1': http10=1;break; 113 case '2': http10=2;break; 114 case 'V': printf(PROGRAM_VERSION"\n");exit(0); 115 case 't': benchtime=atoi(optarg);break; 116 case 'p': 117 /* proxy server parsing server:port */ 118 tmp=strrchr(optarg,':'); 119 proxyhost=optarg; 120 if(tmp==NULL) 121 { 122 break; 123 } 124 if(tmp==optarg) 125 { 126 fprintf(stderr,"Error in option --proxy %s: Missing hostname.\n",optarg); 127 return 2; 128 } 129 if(tmp==optarg+strlen(optarg)-1) 130 { 131 fprintf(stderr,"Error in option --proxy %s Port number is missing.\n",optarg); 132 return 2; 133 } 134 *tmp='\0'; 135 proxyport=atoi(tmp+1);break; 136 case ':': 137 case 'h': 138 case '?': usage();return 2;break; 139 case 'c': clients=atoi(optarg);break; 140 } 141 } 142 143 if(optind==argc) { 144 fprintf(stderr,"webbench: Missing URL!\n"); 145 usage(); 146 return 2; 147 } 148 149 if(clients==0) clients=1; 150 if(benchtime==0) benchtime=60; 151 /* Copyright */ 152 fprintf(stderr,"Webbench - Simple Web Benchmark "PROGRAM_VERSION"\n" 153 "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" 154 ); 155 build_request(argv[optind]);//传送url //结果:request = "GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\nHost: www.baidu.com\r\n\r\n" 156 /* print bench info */ 157 printf("\nBenchmarking: "); 158 switch(method) 159 { 160 case METHOD_GET: 161 default: 162 printf("GET");break; 163 case METHOD_OPTIONS: 164 printf("OPTIONS");break; 165 case METHOD_HEAD: 166 printf("HEAD");break; 167 case METHOD_TRACE: 168 printf("TRACE");break; 169 } 170 printf(" %s",argv[optind]); //argv[optind] 为 "http://www.baidu.com/" 171 switch(http10) //http10==1不执行 172 { 173 case 0: printf(" (using HTTP/0.9)");break; 174 case 2: printf(" (using HTTP/1.1)");break; 175 } 176 printf("\n"); 177 if(clients==1) printf("1 client"); 178 else 179 printf("%d clients",clients); // 180 181 printf(", running %d sec", benchtime); // 182 if(force) printf(", early socket close"); 183 if(proxyhost!=NULL) printf(", via proxy server %s:%d",proxyhost,proxyport); 184 if(force_reload) printf(", forcing reload"); 185 printf(".\n"); 186 return bench(); 187 } 188 189 void build_request(const char *url) 190 { 191 char tmp[10]; 192 int i; 193 194 bzero(host,MAXHOSTNAMELEN); //函数原型 void bzero(void *s,int n)将s所指的区域前n个字节置为零 195 bzero(request,REQUEST_SIZE); 196 197 if(force_reload && proxyhost!=NULL && http10<1) http10=1; 198 if(method==METHOD_HEAD && http10<1) http10=1; 199 if(method==METHOD_OPTIONS && http10<2) http10=2; 200 if(method==METHOD_TRACE && http10<2) http10=2; 201 202 switch(method) 203 { 204 default: 205 case METHOD_GET: strcpy(request,"GET");break; 206 case METHOD_HEAD: strcpy(request,"HEAD");break; 207 case METHOD_OPTIONS: strcpy(request,"OPTIONS");break; 208 case METHOD_TRACE: strcpy(request,"TRACE");break; 209 } 210 211 strcat(request," "); 212 213 if(NULL==strstr(url,"://"))//函数原型:extern char *strstr(char *str1,char *str2)返回str2(不包括\0)在str1中的位置,没有则返回NULL 214 { 215 fprintf(stderr, "\n%s: is not a valid URL.\n",url); 216 exit(2); 217 } 218 if(strlen(url)>1500) 219 { 220 fprintf(stderr,"URL is too long.\n"); 221 exit(2); 222 } 223 if(proxyhost==NULL) 224 if (0!=strncasecmp("http://",url,7)) 225 { 226 fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); 227 exit(2); 228 } 229 /* protocol/host delimiter分隔符 */ 230 i=strstr(url,"://")-url+3;//去掉http://得www.aa.com/ 231 /* printf("%d\n",i); */ 232 233 if(strchr(url+i,'/')==NULL) //查找某字符在字符串首次出现的位置 234 { 235 fprintf(stderr,"\nInvalid URL syntax - hostname don't ends with '/'.\n"); 236 exit(2); 237 } 238 if(proxyhost==NULL) 239 { 240 /* get port from hostname */ 241 if(index(url+i,':')!=NULL && 242 index(url+i,':')<index(url+i,'/')) //函数原型char *index(const char*s,char c)返回c在s中的索引 243 { 244 strncpy(host,url+i,strchr(url+i,':')-url-i); 245 bzero(tmp,10); 246 strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1); 247 /* printf("tmp=%s\n",tmp); */ 248 proxyport=atoi(tmp); 249 if(proxyport==0) proxyport=80; 250 } 251 else 252 {// 函数原型char * strncpy(char *dest,const char *src,size_t n);复制目标字符串src的前n个字符到dest //函数原型int strcspn(char *str,char *accept);返回字符串str开头连续不含字符串accept内的字符数目 如 www.baidu.com/ 返回13*/ 253 strncpy(host,url+i,strcspn(url+i,"/")); 254 } 255 // printf("Host=%s\n",host); 256 strcat(request+strlen(request),url+i+strcspn(url+i,"/")); //request=="GET //" 257 } 258 else 259 { 260 // printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport); 261 strcat(request,url); 262 } 263 if(http10==1) 264 strcat(request," HTTP/1.0"); //"GET // HTTP/1.0 HTTP/1.0 HTTP/1.0" 265 else if (http10==2) 266 strcat(request," HTTP/1.1"); 267 strcat(request,"\r\n"); 268 if(http10>0) 269 strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\r\n");//"GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\n" 270 if(proxyhost==NULL && http10>0) 271 { 272 strcat(request,"Host: "); 273 strcat(request,host); 274 strcat(request,"\r\n"); //request = "GET // HTTP/1.0 HTTP/1.0 HTTP/1.0\r\nUser-Agent: WebBench 1.5\r\nHost: www.baidu.com\r\n" 275 } 276 if(force_reload && proxyhost!=NULL) 277 { 278 strcat(request,"Pragma: no-cache\r\n"); 279 } 280 if(http10>1) 281 strcat(request,"Connection: close\r\n"); 282 /* add empty line at end */ 283 if(http10>0) strcat(request,"\r\n"); 284 // printf("Req=%s\n",request); 285 } 286 287 /* vraci system rc error kod */ 288 static int bench(void) 289 { 290 int i,j,k; 291 pid_t pid=0; 292 FILE *f; 293 294 /* check avaibility of target server */ 295 i=Socket(proxyhost==NULL?host:proxyhost,proxyport); //i==3 296 if(i<0) { 297 fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); 298 return 1; 299 } 300 close(i); 301 /* create pipe */ 302 if(pipe(mypipe)) //建立管道 返回值==0 filedes[0]为管道里的读取端filedes[1]则为管道的写入端 303 { 304 perror("pipe failed."); 305 return 3; 306 } 307 308 /* not needed, since we have alarm() in childrens */ 309 /* wait 4 next system clock tick */ 310 /* 311 cas=time(NULL); 312 while(time(NULL)==cas) 313 sched_yield(); 314 */ 315 316 /* fork childs */ 317 for(i=0;i<clients;i++) 318 { //进入gdb时经过设置 set follow-fork-mode [parent][child]来选择调试父进程或子进程 319 pid=fork(); //fork()函数产生子进程分别执行下面的代码.父进程返回子进程pid>0,子进程返回0,失败返回-1 320 if(pid <= (pid_t) 0) //调试子进程时pid==0,进入if.若调试父进程不进入if 321 { 322 /* child process or error*/ 323 sleep(1); /* make childs faster */ 324 break; 325 } 326 } 327 328 if( pid< (pid_t) 0) 329 { 330 fprintf(stderr,"problems forking worker no. %d\n",i); 331 perror("fork failed."); 332 return 3; 333 } 334 335 if(pid== (pid_t) 0) 336 { 337 /* I am a child */ 338 if(proxyhost==NULL) 339 benchcore(host,proxyport,request); 340 else 341 benchcore(proxyhost,proxyport,request); 342 343 /* write results to pipe */ 344 f=fdopen(mypipe[1],"w"); 345 if(f==NULL) 346 { 347 perror("open pipe for writing failed."); 348 return 3; 349 } 350 /* fprintf(stderr,"Child - %d %d\n",speed,failed); */ 351 fprintf(f,"%d %d %d\n",speed,failed,bytes); 352 fclose(f); 353 return 0; 354 } 355 else 356 { 357 f=fdopen(mypipe[0],"r"); //FILE *fdopen(int filedes,const char *type)用于在一个已经打开的文件描述符(参数1)上按哪一种方式(参数2)打开一个流 358 if(f==NULL) 359 { 360 perror("open pipe for reading failed."); 361 return 3; 362 } 363 setvbuf(f,NULL,_IONBF,0); //为流指定缓冲区。参数1,指定流指针。参数2,为NULL函数自动分配缓冲区。_IONBF表示不使用缓冲区,真是醉了,当即写每次I/O操做 364 speed=0; 365 failed=0; 366 bytes=0; 367 //{i,j,k,speed,failed,bytes,pid} = {10, -1073745752, -1208015616, 0, 0, 0, 10752} 368 while(1) 369 { 370 pid=fscanf(f,"%d %d %d",&i,&j,&k); //从流中读取格式化数据。返回成功添加到参数列表元素的数目 pid:7328变为3 371 if(pid<2) 372 { 373 fprintf(stderr,"Some of our childrens died.\n"); 374 break; 375 } 376 speed+=i; 377 failed+=j; 378 bytes+=k; //{speed, failed,bytes} = {41, 0, 4077115} 379 /* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */ 380 if(--clients==0) break; 381 } 382 fclose(f); 383 //{i,j,k,speed,failed,bytes,pid} = {2, 0, 6041, 141, 0, 1871545, 3} 384 printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n", 385 (int)((speed+failed)/(benchtime/60.0f)), 386 (int)(bytes/(float)benchtime), 387 speed, 388 failed); 389 } 390 return i; 391 } 392 393 void benchcore(const char *host,const int port,const char *req) 394 { 395 int rlen; 396 char buf[1500]; 397 int s,i; 398 struct sigaction sa; //信号产生时触发某些动做咱们就能够安装信号。sigaction安装时有3个参数第一个参数是信号的值,第二个是sigaction结构这个结构说明了信号发生时调用的函数和其它的一些信息:sa_handler =<alarm_handler>,sa_mask = 3085037896...sa_flags = 0, sa_restorer = 0x0} 399 400 /* setup alarm signal handler */ 401 sa.sa_handler=alarm_handler; //alarm_handler警报处理过程 402 sa.sa_flags=0; 403 if(sigaction(SIGALRM,&sa,NULL)) 404 exit(3); 405 alarm(benchtime);//系统中的每一个进程都有一个私有的闹钟。这个闹钟很像一个计时器,能够设置在必定秒数后闹钟。时间一到,时钟就发送一个信号SIGALRM到进程。 406 407 rlen=strlen(req); 408 nexttry:while(1) 409 { 410 if(timerexpired) //定时1s超时退出,gdb调试的时候注意不要超时 411 { 412 if(failed>0) 413 { 414 /* fprintf(stderr,"Correcting failed by signal\n"); */ 415 failed--; 416 } 417 return; 418 } 419 s=Socket(host,port); 420 if(s<0) { failed++;continue;} 421 if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;} //rlen == 65 write() 422 if(http10==0) 423 if(shutdown(s,1)) { failed++;close(s);continue;} 424 if(force==0) 425 { 426 /* read all available data from socket */ 427 while(1) 428 { 429 if(timerexpired) break; //gdb:set var timerexpired=0,防止超时退出 430 i=read(s,buf,1500); //从缓冲区读取数据read() 431 /* fprintf(stderr,"%d\n",i); */ 432 if(i<0) 433 { 434 failed++; 435 close(s); 436 goto nexttry; 437 } 438 else 439 if(i==0) break; 440 else 441 bytes+=i; 442 } 443 } 444 if(close(s)) {failed++;continue;} 445 speed++; 446 } 447 }
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 int Socket(const char *host, int clientPort) //host==www.baidu.com port==80 30 { 31 int sock; 32 unsigned long inaddr; 33 struct sockaddr_in ad; 34 struct hostent *hp; 35 36 //{ad} = {{sin_family = 2, sin_port = 0, sin_addr = {s_addr = 0}, 37 38 memset(&ad, 0, sizeof(ad)); 39 ad.sin_family = AF_INET; 40 41 inaddr = inet_addr(host); 42 if (inaddr != INADDR_NONE) 43 memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr)); 44 else 45 { 46 hp = gethostbyname(host); 47 if (hp == NULL) 48 return -1; 49 memcpy(&ad.sin_addr, hp->h_addr, hp->h_length); 50 } 51 ad.sin_port = htons(clientPort); 52 //ad = {sin_family = 2, sin_port = 20480, sin_addr = {s_addr = 2471192266} 53 sock = socket(AF_INET, SOCK_STREAM, 0); //sock==3n 54 if (sock < 0) 55 return sock; 56 if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) 57 return -1; 58 return sock; 59 } 60 /*---------------------------------------------------------------------- 61 请求链接的客户端套接字(client端): 62 1.调用socket函数建立套接字:int socket(int domain, int type, int protocol) //成功返回文件描述符,失败返回-1 63 2.调用connect函数向服务器端发送链接请求:int connect(int sockfd, struct sockaddr* serveraddr, socklen_t addrlen) //链接时serveraddr 64 3.read()/write():数据交换 65 4.close函数关闭链接 66 ----------------------------------------------------------------------*/
include<getopt.h>//功能:主要用来处理接受的命令行参数argc,argv[]。函数主要包括getopt()和getopt_long(),不用本身处理用户输入的命令行参数了。网络
[指令]webbench -c 10 -t 20 http://www.baidu.com/ 其中argc==6从1开始计数,argv[0-5]分别==webbench -c 10 -t 20 http://...从0开始计数数据结构
getopt()函数原型:int getopt(int argc,char *const argv[],const char *optstring);dom
全局变量 extern char *optarg; extern int optind, opterr, optopt; optarg选项的参数指针,optind这个索引指向argv里当前分析的字符串的下一个索引。socket
getopt()处理以-开头的命令行参数,如optstring="912Vfrt:p:c:?h"表示-9 -V -f -p -c -tide
调用该函数一次返回一个选项,直到结束返回-1。
getopt_long()函数原型:int getopt_long(int argc,char *const argv[],const char *optstring,const struct option *longopts,
webbench中为:
while((opt=getopt_long(argc,argv,"912Vfrt:p:c:?h",long_options,&options_index))!=EOF )//:后表明须要参数
struct option 类型数组
该数据结构中的每一个元素对应了一个长选项,而且每一个元素是由四个域组成。一般状况下,能够按如下规则使用。第一个元素,描述长选项的名称;第二个选项,表明 该选项是否须要跟着参数,须要参数则为1,反之为0。no_argument 0 选项没有参数 required_argument 1 选项须要参数 optional_argument 2 选项参数是可选的;第三个选项,能够赋为NULL;第四个选项,是该长选项对应的短选项名称。另外,数据结构的最后一个元素,要求全部域的内容均为0,即{NULL,0,NULL,0}。
longopts是一个struct结构体实例,webbench中为:
static const struct option long_options[]={
默认的测试时间:30s,主要是给子进程的闹钟,时间到了timerexpired会置1,从内存读的。
父进程与子进程通讯经过管道进行通讯:
1. mypipe[0] : 是读的管道端口; 2. mypipe[1] : 是写的管道端口;
alarm_handler函数功能: 1. 闹钟信号处理函数,当时间到了的时候,timerexpired被置1,表示时间到了; 2. benchcore()中会根据timerexpired值来判断子进程的运行;
建立发送给http服务器的请求头
612 * Socket函数完成的工做: 613 * 1. 转换IP,域名,填充struct sockaddr_in,获取对应的socket描述符; 614 * 2. 链接服务器;
578 * bench函数完成如下功能: 579 * 1. 试探性的尝试一次是否可以正常链接服务器,若是链接失败,也就不必继续后续处理了; 580 * 2. 建立管道,用于父子进程通讯; 581 * 3. 建立clients对应数量的子进程; 582 * 4. 子进程: 583 * 1. 对服务器进行benchtime秒的链接访问,获取对应的failed,speed,bytes值; 584 * 2. 当时间到了benchtime秒之后,打开写管道; 585 * 3. 将子进程本身测试获得的failed,speed,bytes值发送给父进程; 586 * 4. 关闭写管道文件描述符; 587 * 5. 父进程: 588 * 1. 打开读管道; 589 * 2. 设置管道一些参数,初始化父进程的failed,speed,bytes变量; 590 * 3. while循环不断去获取子进程传输过来的数据,直到子进程所有退出; 591 * 4. 关闭读管道; 592 * 5. 对数据进行处理,并打印输出; 593 */
740 * benchcore函数完成功能: 741 * 1. 注册闹钟处理函数,设置闹钟时间,具体时间由benchtime给出,默认是30s; 742 * 2. while循环里判断闹钟时间是否到了,若是到了退出循环; 743 * 3. while循环里链接服务器; 744 * 4. while循环里发送http请求头给服务器; 745 * 5. while循环里判断是否须要接受服务器数据; 746 * 6. 关闭与服务器的链接; 747 * 7. 在整个过程当中,如下变量会统计在benchtime给出的时间内的一些信息: 748 * 1. failed : 链接服务器失败和传输数据过程当中失败的链接数; 749 * 2. speed : 正常链接服务器,而且正常传输数据的链接数 750 * 3. bytes : 从服务器获取到的字节数; 751 */