一、程序说明: 本程序对上述png服务器性能进行测试。(开发于FreeBSD,并可编译运行于Windows Cygwin环境)。
二、使用说明:(类似于ab) st [options] [http://]hostname/path -n requests Number of requests to perform -c concurrency Number of multiple requests to make -v Print version number and exit -h Display usage information (this message)
三、源代码: /****************************************************************************** * Copyright (C) 2004-2005 XiongBin Xiong All rights reserved * References: Stevens,W.R. 1992. Advanced Programming in the UNIX Environment. * Addison-Wesley. * Stevens,W.R. 1998. UNIX Network Programming Volum1. * Prentice Hall PTR. */
#include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/time.h> #include <netinet/in.h> #include <fcntl.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <netdb.h> #include <signal.h>
#define BUFFSIZE 65535 #define MAXLINE 4096
static int requests=1; /* 在测试会话中所执行的请求个数,默认为一个 */ static int concurrency=1; /* 并发级别,一次产生的请求个数,默认是一次一个 */ static int done; /* 在测试会话中所完成的请求个数 */ static int recvdatas=0; /* 接收的总数据量 */ static char *hostname,*pathname; /* 主机名、请求路径名 */ static struct timeval take_time; /* 完成所有请求所需的时间 */ static int lflag; /* 子进程结束计数标志 */
int parse_url(char *); /* 将URL分解为hostname和pathname */ int test(void); /* 开始进行测试 */ int con_test(int); /* 并发进程测试 */ int fork_do(void); /* 测试程序 */ int record(int); /* 记录测试数据 */ int lock_reg(int,int,int,off_t,int,off_t); /* 使用记录锁对共享文件进行保护 */ static void output_results(void); /* 输出结果 */ static void sig_chld(int); /* 处理子进程SIGCHLD信号 */ static void usage(const char *); /* 提示信息 */ static void err(char *); /* 出错处理函数 */ static void copyright(void); /* 版本信息 */
int main(int argc, char *argv[]) { int flag; char *url; FILE *fp; if(argc<2||argc>6){ printf("Invalid input\n"); usage(argv[0]); exit(1); }
opterr=0; while((flag=getopt(argc,argv,"n:c:hv"))!=EOF){ switch(flag){ case 'n': requests=atoi(optarg); /* 总请求数 */ if(requests<=0) err("Invalid number of requests"); break; case 'c': concurrency=atoi(optarg); /* 并发请求的级别,即每个并发请求中同时发起的请求数 */ if(concurrency<=0) err("Invalid number of concurrency"); break; case 'h': usage(argv[0]); /* 帮助信息 */ break; case 'v': copyright(); /* 版本信息 */ exit(0); case '?': printf("unrecognized option: -%c\n",optopt); /* 错误的参数 */ usage(argv[0]); exit(1); } } if(requests<concurrency) /* 并发级别要小于总请求数 */ err("Invalid number of requests or concurrency"); done=requests; fp=fopen("temp.log","wb"); fprintf(fp,"%d\t%d",done,recvdatas); /* 设置文件初值 */ fclose(fp);
if(optind>=argc) err("Invalid input"); else url=argv[optind];
if(parse_url(url)) /* 将URL分解为hostname和pathname */ err("Invalid URL address"); copyright(); write(STDOUT_FILENO,"testing...",10);
test(); /* 开始进行测试 */ output_results(); /* 结果输出 */ unlink("temp.log"); /* 删除临时文件 */
exit(0); }
/*----------------------------------------------------------------*/ /* 将URL分解为hostname和pathname */ int parse_url(char *url) { char *p,*pp,*urlptr;
urlptr=url; if(strncmp(urlptr,"http://",7)==0) /* 寻找"http://"处 */ urlptr=urlptr+7; if((p=strstr(urlptr,"\n"))!=0) /* 寻找"\n"处 */ *p='\0'; p=strchr(urlptr,'/'); /* 寻找'/'处 */ if(p==0) return(1); pathname=p+1; /* 获取pathname */ *p='\0'; hostname=urlptr; /* 获取hostname */ return(0); }
/*-----------------------------------------------------------------*/ /* 开始进行测试 */ int test(void) { int i,n,m; struct timeval start_time,end_time;
n=requests/concurrency; /* n为并发循环次数 */ m=requests%concurrency; /* 当上面不能整除时,m为最后一次并发请求的并发级别 */
gettimeofday(&start_time,NULL); /* 获取测试开始时间 */ for(i=0;i<n;i++){ con_test(concurrency); /* 并发测试 */ } con_test(m); /* 剩余并发测试 */ gettimeofday(&end_time,NULL); /* 获取测试结束时间 */
if(end_time.tv_usec<start_time.tv_usec){ take_time.tv_usec=end_time.tv_usec+1000000-start_time.tv_usec; end_time.tv_sec--; }else take_time.tv_usec=end_time.tv_usec-start_time.tv_usec; take_time.tv_sec=end_time.tv_sec-start_time.tv_sec; /* 测试总耗时 */
return; }
/*-----------------------------------------------------------------*/ /* 并发进程测试 */ int con_test(int level) { int i,pid;
lflag=level; signal(SIGCHLD,sig_chld); /* 获取SIGCHLD信号 */ for(i=0;i<level;i++){ if((pid=fork())<0){ printf("con_test(): fork error,\trefork\n"); i--; } else if(pid==0){ /* 生成并发子进程 */ fork_do(); /* 测试 */ exit(0); } } while(lflag); /* 等待所有并发子进程结束 */ return(0); }
/*------------------------------------------------------------------*/ /* 测试程序 */ int fork_do(void) {
int i,n,l,k,pid,ppid; struct sockaddr_in servaddr; char buf[BUFFSIZE],pngname[MAXLINE],cpid[8],*p,*h,rbuf[MAXLINE]="GET /"; FILE *ffp; int sockfd; struct hostent *hp; struct in_addr **ptr; hp=gethostbyname(hostname); /* 获取IP地址 */ ptr=(struct in_addr **)hp->h_addr_list;
sockfd=socket(AF_INET,SOCK_STREAM,0); bzero(&servaddr,sizeof(servaddr)); /* 初始化套接口地址结构 */ servaddr.sin_family=AF_INET; servaddr.sin_port=htons(80); memcpy(&servaddr.sin_addr,*ptr,sizeof(struct in_addr)); l=0;
if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0) printf("connect error\n"); else{ /* 连接成功 */ strcat(rbuf,pathname); strcat(rbuf," HTTP/1.0"); /* 形成GET请求命令 */
n=strlen(rbuf); if(write(sockfd,rbuf,n)!=n) /* 发送GET请求至服务器 */ err("fork_do(): write error"); l=recv(sockfd,buf,sizeof(buf),0); /* 接收服务器返回数据 */
if((pid=getpid())<0) err("fork_do(): getpid error"); sprintf(cpid,"%d",pid);
strcpy(pngname,"clipng"); strcat(pngname,cpid); strcat(pngname,".png"); /* 形成带有PID的文件名 */
ffp=fopen(pngname,"wb"); /* 创建文件 */ unlink(pngname);
p=strstr(buf,"\n\n"); h=buf; k=p-h+2; /* 定位在PNG数据区开始处 */ fwrite(&buf[k],sizeof(char),l-k,ffp); /* 写入数据 */ }
record(l); /* 记录本次请求的数据 */
close(sockfd); fclose(ffp); return(0); }
/*------------------------------------------------------------------*/ /* 记录测试数据 */ int record(int recvn) { int fd,a,b; FILE *fp;
if((fp=fopen("temp.log","r+"))==NULL) /* 读写方式打开文件 */ return(1); fd=fileno(fp);
while((lock_reg(fd,F_SETLK,F_WRLCK,0,SEEK_SET,0))==-1); /* 给文件加锁 */ fscanf(fp,"%d\t%d",&a,&b); /* 从文件得到当前数据 */ if(recvn) b+=recvn; /* 如果connect成功,接收数据量累加 */ else a--; /* connect失败,完成次数减一 */ rewind(fp); fprintf(fp,"%d\t%d",a,b); /* 写入文件 */ lock_reg(fd,F_SETLK,F_UNLCK,0,SEEK_SET,0); /* 解锁 */
fclose(fp); return(0); }
/*-------------------------------------------------------------------*/ /* 使用记录锁对共享文件进行保护 */ int lock_reg(int fd,int cmd,int type,off_t offset,int whence,off_t len) { struct flock lock;
lock.l_type=type; /* F_RDLCK,F_WRLCK,F_UNLCK */ lock.l_start=offset; /* byte offset, 起始处的相对偏移量 */ lock.l_whence=whence; /* SEEK_SET,SEEK_CUR,SEEK_END */ lock.l_len=len; /* 区域的长度(为0表示到最大位置为止) */
return(fcntl(fd,cmd,&lock)); /* 使用记录锁 */ }
/*--------------------------------------------------------------------*/ /* 输出结果 */ static void output_results(void) { float takentime; FILE *fp;
fp=fopen("temp.log","r"); fscanf(fp,"%d\t%d",&done,&recvdatas); /* 从文件中获取数据 */ takentime=((float)take_time.tv_sec)+((float)take_time.tv_usec)/1000000.0F; /* 格式化为整数+小数形式 */
printf("\n\n"); printf("Server Hostname: %s\n",hostname); /* 主机名 */ printf("Server Port: %hd\n",80); /* 端口 */ printf("Document Path: /%s\n",pathname); /* 请求路径名 */ printf("\n"); printf("Total Requests: %d\n",requests); /* 请求数 */ printf("Concurrency Level: %d\n",concurrency); /* 并发数 */ printf("Time taken for tests: %ld.%03ld seconds\n",take_time.tv_sec,take_time.tv_usec); /* 总耗时 */ printf("Complete requests: %ld\n",done); /* 完成请求数 */ printf("Failed requests: %ld\n",(requests-done)); /* 失败请求数 */ printf("Total transferred: %ld Bytes\n",recvdatas); /* 总接收数据量 */ printf("\n");
if(takentime){ printf("Requests per second: %.2f [#/sec] (mean)\n",(float)(done/takentime)); /* 每秒完成请求数 */ printf("Time per request: %.3f [ms] (mean)\n",(float)(1000*concurrency*takentime/done));/* 完成一个并发请求的时间 */ printf("Time per request: %.3f [ms] (mean, across all concurrent requests)\n",(float)(1000*takentime/done)); /* 完成一个请求的时间 */ printf("Transfer rate: %.2f [Kbytes/sec] received\n",(float)(recvdatas/takentime/1024)); /* 每秒传输数据量 */ } printf("\n"); }
/*---------------------------------------------------------------------*/ /* 处理子进程SIGCHLD信号 */ static void sig_chld(int signo) { pid_t pid; int stat;
while((pid=waitpid(-1,&stat,1))>0) /* 处理已结束子进程状态,防止子进程Zombie状态出现 */ lflag--; /* 标志减一 */
return; }
/*---------------------------------------------------------------------*/ /* 提示信息 */ static void usage(const char *progname) { fprintf(stderr, "Usage: %s [options] [http://]hostname/path\n", progname); fprintf(stderr, "Options are:\n"); fprintf(stderr, " -n requests Number of requests to perform\n"); fprintf(stderr, " -c concurrency Number of multiple requests to make\n"); fprintf(stderr, " -v Print version number and exit\n"); fprintf(stderr, " -h Display usage information (this message)\n"); exit(0); }
/*----------------------------------------------------------------------*/ /* 出错处理函数 */ static void err(char *s) { fprintf(stderr, "%s\n", s); exit(1); }
/*----------------------------------------------------------------------*/ /* 版本信息 */ static void copyright(void) { printf("This is ServerTest for my pngserver, Version 1.0\n"); printf("Copyright (C) 2004-2005 XiongBin Xiong All rights reserved\n"); printf("[email protected]\n"); printf("\n"); }
/* end all */ 
|