对上述png服务器性能测试的程序

类别:编程语言 点击:0 评论:0 推荐:

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

本文地址:http://com.8s8s.com/it/it23673.htm