一、程序說明:
本程序對上述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 */