對上述png服務器性能測試的程序

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章