【webbench】源码剖析

socket.c

/* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $
 *
 * This module has been modified by Radim Kolar for OS/2 emx
 */

/***********************************************************************
  module:       socket.c
  program:      popclient
  SCCS ID:      @(#)socket.c    1.5  4/1/94
  programmer:   Virginia Tech Computing Center
  compiler:     DEC RISC C compiler (Ultrix 4.1)
  environment:  DEC Ultrix 4.3 
  description:  UNIX sockets code.
 ***********************************************************************/

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

int Socket(const char *host, int clientPort)
{
	int sock;
	unsigned long inaddr;
/***************************************
  struct sockaddr_in{
	short int sin_family  //协议族,只能是AF_INET
	unsigned short int sin_port //端口号
	struct in_addr sin_addr;    //IP地址
	unsigned char sin_zero[8];  //保持sockaddr_in和sockaddr一致
  };
  struct in_addr{
	unsigned long s_addr;    //按照网络字节顺序存储IP地址
  };
  **************************************/
	struct sockaddr_in ad;
	struct hostent *hp;
/****************************************
	struct hostent{
	  char  *h_name;     //主机名,官方域名
	  char **h_aliases;  //主机所有别名
	  int    h_addrtype; //IP地址类型
	  int    h_length;   //IP地址长度
	  char **h_addr_list;//IP地址,网络字节序
	};
	struct hostent *gethostbyname(const char * hostname);
  **************************************/

/*=== 设置sockaddr_in ===*/
	/* 初始化地址 */
	memset(&ad, 0, sizeof(ad));
	/*set---sin_family*/
	ad.sin_family = AF_INET;
	/*set---sin_addr*/
	/* 将IP地址转换为长整型,如果是.分隔的ip,如:192.168.22.30 */
	inaddr = inet_addr(host);
	if(inaddr != INADDR_NONE) //有效地址
		memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
	/* 如果是字符串格式的,如:www.baidu.com,用gethostbyname()*/
	else
	{
		hp = gethostbyname(host);
		if(hp == NULL)
			return -1;
		/* hostent中的*h_addr是h_addr_list[0] */
		memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
	}
	/*set---sin_port*/
		/* htons将无符号整型转换为网络字节序 */
	ad.sin_port = htons(clientPort);
/*==== 创建socket套接字,连接 ====*/
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0)
		return sock;
	/* 建立连接,三次握手 */
	if(connect(sock, (struct sockaddr*)&ad, sizeof(ad)) < 0)
		return -1;
	return sock;
}

webbench.c

/*
 * (C) Radim Kolar 1997-2004
 * This is free software, see GNU Public License version 2 for
 * details.
 *
 * Simple forking WWW Server benchmark:
 *
 * Usage:
 *   webbench --help
 *
 * Return codes:
 *    0 - sucess
 *    1 - benchmark failed (server is not on-line)
 *    2 - bad param
 *    3 - internal error, fork failed
 * 
 */ 
#include "socketTest.c"
#include <unistd.h>
#include <sys/param.h>
#include <rpc/types.h>
#include <getopt.h>
#include <strings.h>
#include <time.h>
#include <signal.h>

/* values */
volatile int timerexpired = 0; //修饰不同线程访问的变量
int speed = 0;
int failed = 0;
int bytes = 0;
/* globals */
/* http协议版本号 */
int http10 = 1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1*/
/* Allow: GET, HEAD, OPTIONS, TRACE */
#define METHOD_GET 0
#define METHOD_HEAD 1
#define METHOD_OPTIONS 2
#define METHOD_TRACE 3
#define PROGRAM_VERSION "1.5" //版本号
int method = METHOD_GET; //指定http方法
int client = 1;          //并发数
int force = 0;           //是否等待服务器应答,默认不应答
int force_reload = 0;    //
int proxyport = 80;      //代理服务器端口号,默认80
char *proxyhost = NULL;  //代理服务器地址
int benchtime = 30;      //运行时间,默认30s,可以用-t指定
/* internal */
int mypipes[2];
char host[MAXHOSTNAMELEN];
#define REQUEST_SIZE 2048
char request[REQUEST_SIZE];

/*struct option{
  const char* name;    //参数名称
  int         has_arg; //指明是否带数值,0-不带参数 1-必须带参数 2-可选参数
  int        *flag;    //当flag为空时,直接将val的值从getopt_long的返回值返回出去,当非空时,val被赋值到flag指向的整型数中,而函数返回0
  int         val;     //用于指定函数找到时的返回值
};        //getopt_long第三个参数,option结构体数组*/
static const struct option long_options[]=
{
//参数名 |是否带参数 |返回方式 |返回值
  {"force",no_argument,&force,1},  //force, 0, force, 1
  {"reloac",no_argument,&force_reload,1},
  {"time",required_argument,NULL,'t'}.//time, 1, NULL, 't'
  {"help",no_argument,NULL,'?'},
  {"http09",no_argument,NULL,'9'},
  {"http10",no_argument,NULL,'1'},
  {"http11",no_argument,NULL,'2'},
  {"get",no_argument,&method,METHOD_GET},
  {"head",no_argument,&method,METHOD_HEAD},
  {"options",no_argument,&method,METHOD_OPTIONS},
  {"trace",no_argument,&method,METHOD_TRACE},
  {"version",no_argument,NULL,'V'},
  {"proxy",required_argument,NULL,'p'},//proxy, 2, NULL, 'p'
  {"clients",required_argument,NULL,'c'},
  {NULL,0,NULL,0}  //没传入第三个参数的情况
};

/* prototypes */
static void build_request(const char *url);
static int bench(void);
static void benchcore(const char* host,const int port, const char *requeste);

static void alarm_handler(int signal)
{
	timerexpired = 1;
}

static void usage(void)
{
	fprintf(stderr,
	"webbench [option]... URL\n"
	"  -f|--force               Don't wait for reply from server.\n"
	"  -r|--reload              Send reload request - Pragma: no-cache.\n"
	"  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\n"
	"  -p|--proxy <server:port> Use proxy server for request.\n"
	"  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\n"
	"  -9|--http09              Use HTTP/0.9 style requests.\n"
	"  -1|--http10              Use HTTP/1.0 protocol.\n"
	"  -2|--http11              Use HTTP/1.1 protocol.\n"
	"  --get                    Use GET request method.\n"
	"  --head                   Use HEAD request method.\n"
	"  --options                Use OPTIONS request method.\n"
	"  --trace                  Use TRACE request method.\n"
	"  -?|-h|--help             This information.\n"
	"  -V|--version             Display program version.\n"
	);
};
int main(int argc, char *argv[])
{
	int opt = 0;
	int options_index = 0;
	char *tmp = NULL;

	if(1 == argc)
	{
		usage();
			return 2;   //2 - bad param
	}
/*--getopt_long函数:
int getopt_long(int argc,char* const argv[], //argc,argv    -main
  const char *optstring,//命令行传入的参数,有:表示可以可以指定值
  const struct option *longopts,//指向一个由option结构体组成的数组 
  int *longindex);//如果非空,指向的变量记录当前找到的参数符合longopts里对应的下标值
*/
	while((opt=getopt_long(argc, argv, "912Vfrt:p:c:?h", long_options, &options_index))!=EOF)
	{
	  switch(opt)
	  {
		case  0 :                              break;
		case 'f': force = 1;                   break;
		case 'r': force_reload = 1;            break;
		case '9': http10 = 0;                  break;
		case '1': http10 = 1;                  break;
		case '2': http10 = 1;                  break;
		case 'V': printf(PROGRAM_VERSION"\n"); exit(0);
		case 't': benchtime = atoi(optarg);    break;
		case 'p':
			/* proxy server parsing server:port */
			tmp = strrchr(optarg, ':');
			proxyhost = optarg;
			if(tmp == NULL)
				break;
			if(tmp == optarg)
			{
				fprintf(stderr, "Error in option --proxy %s: Missing hostname.\n", optarg);
				return 2;
			}
			if(tmp == optarg + strlen(optarg) - 1)
			{
				fprintf(stderr, "Error in options --proxy %s: Port number is missing.\n",optarg);
				return 2;
			}
			*tmp = '\0';
			proxyport = atoi(tmp + 1);		   break;
		case ':':
		case 'h':
		case '?': usage(); return 2;           break;
		case 'c': clients = atoi(optarg);      break;
	  }
	}

	if(optind == argc) {//如果下一个参数的下标和参数个数相同
		fprintf(stderr, "webbench: Missing URL!\n");
		usage();
		return 2;
	}

	if(clients == 0) clients = 1;
	if(benchtime == 0) benchtime = 60;
	/*Copyright*/
	fprintf(stderr,"WebBench - Simple Web Benchmark "PROGRAM_VERSION"\n"
			"Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n");
	build_request(argv[optind]);
	/* print bench info */
	printf("\nBenchmarking: ");
	switch(method)
	{
		case METHOD_GET:
		default:
			printf("GET"); break;
		case METHOD_OPTIONS:
			printf("OPTIONS"); break;
		case METHOD_HEAD:
			printf("HEAD"); break;
		case METHOD_TRACE:
			printf("TRACE"); break;
	}
	printf(" %s", argv[optind]);
	switch(http10)
	{
		case 0: printf(" (using HTTP/0.9)");break;
		case 2: printf(" (using HTTP/1.0)");break;
	}
	printf("\n");
	if(clients==1) printf("1 client");
	else
		printf("%d clients", clients);

	printf(", running %d sec", benchtime);
	if(force) printf(", early socket close");
	if(proxyhost!=NULL) printf(", via proxy server %s:%d", proxyhost,proxyport);
	if(force_reload) printf(", forcing reload");
	printf(".\n");
	return bench();
}

/*====================
   创建URL请求连接
   @url:url地址
   创建好的请求放在全局变量request中
====================== */
void build_request(const char *url)
{
	char tmp[10];
	int  i;
	
	//请求地址和请求连接清0
	bzero(host, MAXHOSTNAMELEN);  //初始化host,置为0
	bzero(request, REQUEST_SIZE); //初始化requeste,置为0

	if(force_reload && proxyport!=NULL && http10<1) http10=1;
	if(method==METHOD_HEAD && http10<1) http10=1;
	if(method==METHOD_OPTIONS && http10<2) http10=2;
	if(method==METHOD_TRACE && http10<2) http10=2;

	switch(method)
	{
		default:
		case METHOD_GET: strcpy(request,"GET");        break;
		case METHOD_HEAD: strcpy(request,"HEAD");      break;
		case METHOD_OPTIONS: strcpy(request,"OPTIONS");break;
		case METHOD_TRACE: strcpy(request,"TRACE");    break;					 
	}

	strcat(request," ");           //拼接字符

	if(NULL == strstr(url,"://"))  //查找子串
	{
		fprintf(stderr, "\n%s: is not a valid URL.\n", url);
		exit(2);
	}
	if(strlen(url) > 1500)
	{
		fprintf(stderr,"URL is too long.\n");
		exit(2);
	}
	if(proxyhost == NULL)          //代理是否为空
		if(0 != strncasecmp("http://",url,7)) //比较前7个字符串,相同返回0
		{
			fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for others.\n");
			exit(2);
		}
	/* protocol/host delimiter */
	i = strstr(url,"://")-url+3;

	if(strchr(url+i,'/') == NULL)
	{
		fprintf(stderr,"\nInvalid URL syntax -hostname don't ends with '/'.\n");
		exit(2);
	}

	if(proxyhost == NULL)
	{
	/* get port from hostname */
		if(index(url+i,':')!=NULL &&
		   index(url+i,':')<index(url+i,'/')) //判断是否给出端口号
		{
		// 将://到下一个:间字符复制给host
			strncpy(host, url+i, strchr(url+i,':')-url-i);//取出主机地址
			bzero(tmp, 10);
			strncpy(tmp, index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);   //tmp暂存端口号
			proxyport = atoi(tmp);  //端口号转换为int
			if(proxyport == 0) proxyport = 80;
		} else {
			strncpy(host, url+i, strcspn(url+i,"/"));
		}
		strcat(request+strlen(request), url+i+strcspn(utl+i,"/"));
	}else
	{
		strcat(request,url);
	}
	if(http10 == 1)
		strcat(request," HTTP/1.0");
	else if(http10 == 2)
		strcat(request," HTTP/1.1");
	strcat(request,"\r\n");
	if(http10>0)
		strcat(request,"User-Agent: WebBench "PROGRAM_VERSION"\t\n");
	if(proxyhost==NULL && http10>0)
	{
		strcat(request,"Host: ");
		strcat(request, host);
		strcat(request, "\r\n");
	}
	if(force_reload && proxyhost!=NULL)
	{
		strcat(request, "Pragma: no-cache\r\n");
	}
	if(http10>1)
		strcat(request,"Connection: close\r\n");
	if(http10>0) strcat(request, "\r\n");
}

/* vraci system rc error kod */
/*=================
  创建管道和子进程,对http请求进行测试
 ================== */
static int bench(void)
{
	int i,j,k;
	pid_t pid=0;
	FILE *f;

	/* check avaibility of target server */
	/* 是否有代理服务器 */
	i = Socket(proxyhost=NULL? host:proxyhost, proxyport);
	/* 建立连接失败 */
	if(i < 0){
		fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n");
		return 1;
	}
	close(i); //关闭socket

	/* create pipe */
	if(pipe(mypipe))
	{
		perror("pipe failed.");
		return 3;
	}

	/* not needed, since we have alarm() in childrens */
	/* wait 4 next system clock tick */
    /*
	cas=time(NULL);
	while(time(NULL)==cas)
    sched_yield();
	*/

	/* fork childs */
	for(i=0; i<clients; ++i)
	{
		pid = fork();
		if(pid <= (pid_t) 0)
		{
			/* child process or error */
			sleep(1); //make childs faster
			break;
		}
	}

	if( pid < (pid_t) 0)
	{
		fprintf(stderr, "problems forking worker no. %d\n", i);
		perror("fork failed.")
		return 3;
	}

	if(pid == (pid_t) 0)
	{
		/* I am a child */
		if(proxyhost == NULL)
			benchcore(host,proxyport, request);
		else
			benchcore(proxyhost, proxyport, request);
		/* write results to pipe*/
		f = fdopen(mypipe[1], "w");
		if(f == NULL)
		{
			perror("open pipe for writing failed.");
			return 3;
		}

		fprintf(f, "%d %d %d\n", speed, failed, bytes);
		fclose(f);
		return 0;
	}else
	{
		f = fdopen(mypipe[0], "r");
		if(f == NULL)
		{
			perror("open pipe for reading failed.");
			return 3;
		}
		setvbuf(f, NULL, _IONBF, 0); //setvbuf
		speed = 0;
		failed = 0;
		bytes = 0;
		while(1)
		{
			pid = fscanf(f, "%d %d %d",&i,&j,&k);
			if(pid < 2)
			{
				fprintf(stderr,"Some of our childrens died.\n");
				break;
			}
			speed  += i;
			failed += j;
			bytes  += k;

			if(--clients == 0) break;
		}
		fclose(f);

		printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n",
			(int)((speed+failed)/(benchtime/60.0f)),
			(int)(bytes/(float)benchtime),
			speed,
			failed);
	}
	return i;
}

void benchcore(const char *host, const int port, const char *req)
{
	int rlen;
	char buf[1500];
	int s,i;
	struct sigaction sa;
/*=========sigaction============
int sigaction(int sig, const struct sigaction *act, 
			struct sigaction *oact);
	struct sigaction
	{
		void (*)int sa_handle;
		sigset_t    sa_mask;
		int         sa_flags;
	};
  ==============================*/
	/* setup alarm signal handler */
	sa.sa_handle = alarm_handler;
	sa.sa_flags = 0;
	if(sigaction(SIGALRM, &sa, NULL))
		exit 3;
	alarm(benchtime);

	rlen = strlen(req);
nexttry:
	while(1)
	{
		if(timerexpired)
		{
			if(failed > 0)
			{
				failed--;
			}
			return;
		}
		s = Socket(host, port);
		if(s < 0) { failed++; continue; }
		if(rlen != write(s, req, rlen)) { failed++; close(s); continue;}
		if(http10 == 0)
			if(shutdown(s, 1)) { failed++; close(s); continue; }
		if(force == 0)
		{
			while(1)
			{
				if(timerexpired) break;
				i = results(s, buf, 1500);
				if(i < 0)
				{
					failed++;
					close(s);
					goto nexttry;
				}else {
					if( i==0 ) break;
					else bytes += i;
				}
			}
		}
		if(close(s)) {failed++; continue;}
		speed++;
	}
}



1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REAdMe.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看READme.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值