webbench源码阅读
学习内容
一共五百多行代码,其中包含了linux编程常用的API。可以通过学习源码,把不熟悉的API练习练习。
1 如何使用webbench
(1)查看参数帮助
(2)运行方法
即以上模拟30个客户端在30秒期间并发请求百度,结果如下:
每分钟平均有1532次请求连接,服务器每秒传输字节为4039230,在30秒期间请求连接成功为766次,失败0次。
2 源码常用函数练习
(1) 选项参数
int getopt_long(int argc, char * const argv[],const char *optstring, const struct option *longopts,int *longindex);
函数中的argc和argv通常直接从main()的两个参数传递而来。optsting是选项参数组成的字符串:
option结构数组,option结构称为长选项表,其声明如下:
struct option
{
const char *name;
int has_arg;
int *flag;
int val;
};
结构中的元素解释如下:
const char *name:选项名,前面没有短横线。譬如"help"、"verbose"之类。
int has_arg:描述长选项是否有选项参数,如果有,是哪种类型的参数,其值见下表: 符号常量 数值
含义
no_argument 0 选项没有参数
required_argument 1 选项需要参数
optional_argument 2 选项参数是可选的
int *flag:
如果该指针为NULL,那么getopt_long返回val字段的值;
如果该指针不为NULL,那么会使得它所指向的结构填入val字段的值,同时getopt_long返回0 int val:
如果flag是NULL,那么val通常是个字符常量,如果短选项和长选项一致,那么该字符就应该与optstring中
字符串optstring可以下列元素:
1.单个字符,表示选项,
2.单个字符后接一个冒号:表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。
3 单个字符后跟两个冒号,表示该选项后可以有参数也可以没有参数。如果有参数,参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。(这个特性是GNU的扩张)。
optstring是一个字符串,表示可以接受的参数。例如,"a:b:cd",表示可以接受的参数是a,b,c,d,其中,a和b参数后面跟有更多的参数值。(例如:-a host --b name)
最后一个参数:longindex参数一般赋为NULL即可;如果没有设置为NULL,那么它就指向一个变量,这个变量会被赋值为寻找到的长选项在longopts中的索引值,这可以用于错误诊断。
函数使用方法:
View Code
(3)信号相关API
案例https://www.cnblogs.com/yxk529188712/p/4983565.html
(4)IO相关
fwrite()
fread()
read()
(5)管道
int pipe(int fd[2]) 成功返回0,失败返回-1
管道和有名管道是进程间通信机制之一
管道是半双工,所以双方通信需要建立两个通道
FILE * fdopen(int fildes,const char * mode);
fdopen取一个现存的文件描述符(我 们可能从 o p e n , d u p , d u p 2 , f c n t l或p i p e函数得到此文件描述符) ,并使一个标准的I / O流与该描述符相结合。此函数常用于由创建管道和网络通信通道函数获得的描述符。因为这些特殊类型的文件不能用标准I/O fopen函数打开,首先必须先调用设备专用函数以获得一个文件描述符,然后用f d o p e n使一个标准I / O流与该描述符相结合。
fdopen()会将参数fildes 的文件描述符,转换为对应的文件指针后返回。参数mode 字符串 则代表着文件指针的流形态,此形态必须和原先文件描述词读写模式相同。
View Code
2 执行流程
3 源码注释
/*
* (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 "socket.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 */
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;
int clients=1;
int force=0;
int force_reload=0;
int proxyport=80;
char *proxyhost=NULL;
int benchtime=30;/* internal */
int mypipe[2];
char host[MAXHOSTNAMELEN];
#define REQUEST_SIZE 2048
char request[REQUEST_SIZE];static const struct option long_options[]=
{{"force",no_argument,&force,1},{"reload",no_argument,&force_reload,1},{"time",required_argument,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'},{"clients",required_argument,NULL,'c'},{NULL,0,NULL,0}
};/* prototypes */
static void benchcore(const char* host,const int port, const char *request);
static int bench(void);
static void build_request(const char *url);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(argc==1){usage();return 2;}
//getopt_long 参数处理 根据不同的选项对应不同的操作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=2;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 option --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;}//默认clients用户为1if(clients==0) clients=1;//默认压测30sif(benchtime==0) benchtime=30;/* 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 request info ,do it in function build_request/*printf("Benchmarking: ");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.1)");break;}printf("\n");*/printf("Runing info: ");if(clients==1) printf("1 client");elseprintf("%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();
}//构造请求
void build_request(const char *url)
{char tmp[10];int i;//bzero(host,MAXHOSTNAMELEN);//bzero(request,REQUEST_SIZE);memset(host,0,MAXHOSTNAMELEN);memset(request,0,REQUEST_SIZE);if(force_reload && proxyhost!=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;//不同的构造头方法/*http头部格式请求方法 空格 URL 空格 协议版本 \r\n头部字段名 值 \r\n\r\n*/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);}//url限制大小为1500if(strlen(url)>1500){fprintf(stderr,"URL is too long.\n");exit(2);}if (0!=strncasecmp("http://",url,7)) { 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,'/')){strncpy(host,url+i,strchr(url+i,':')-url-i);//bzero(tmp,10);memset(tmp,0,10);strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);/* printf("tmp=%s\n",tmp); */proxyport=atoi(tmp);if(proxyport==0) proxyport=80;} else{strncpy(host,url+i,strcspn(url+i,"/"));}// printf("Host=%s\n",host);strcat(request+strlen(request),url+i+strcspn(url+i,"/"));} else{// printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);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"\r\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");/* add empty line at end */if(http10>0) strcat(request,"\r\n"); printf("\nRequest:\n%s\n",request);
}/* vraci system rc error kod */
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);/* 创建管道 */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 *///多少个用户多少个进程 fork一次返回两个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);elsebenchcore(proxyhost,proxyport,request);/* write results to pipe *///构造完请求得到结果写入管道f=fdopen(mypipe[1],"w");if(f==NULL){perror("open pipe for writing failed.");return 3;}/* fprintf(stderr,"Child - %d %d\n",speed,failed); */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);//不使用缓冲。每个 I/O 操作都被即时写入。buffer 和 size 参数被忽略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;/* fprintf(stderr,"*Knock* %d %d read=%d\n",speed,failed,pid); */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;/* setup alarm signal handler */sa.sa_handler=alarm_handler;sa.sa_flags=0;if(sigaction(SIGALRM,&sa,NULL))exit(3);alarm(benchtime); // after benchtime,then exitrlen=strlen(req);nexttry:while(1){if(timerexpired){if(failed>0){/* fprintf(stderr,"Correcting failed by signal\n"); */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) {/* read all available data from socket */while(1){if(timerexpired) break; i=read(s,buf,1500);/* fprintf(stderr,"%d\n",i); */if(i<0) { failed++;close(s);goto nexttry;}elseif(i==0) break;elsebytes+=i;//read返回的是字节数 所以在此可以统计字节数}}if(close(s)) {failed++;continue;}speed++;}
}
webbench源码阅读相关推荐
- 应用监控CAT之cat-client源码阅读(一)
CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...
- centos下将vim配置为强大的源码阅读器
每日杂事缠身,让自己在不断得烦扰之后终于有了自己的清静时光来熟悉一下我的工具,每次熟悉源码都需要先在windows端改好,拖到linux端,再编译.出现问题,还得重新回到windows端,这个过程太耗 ...
- 源码阅读:AFNetworking(十六)——UIWebView+AFNetworking
该文章阅读的AFNetworking的版本为3.2.0. 这个分类提供了对请求周期进行控制的方法,包括进度监控.成功和失败的回调. 1.接口文件 1.1.属性 /**网络会话管理者对象*/ @prop ...
- 源码阅读:SDWebImage(六)——SDWebImageCoderHelper
该文章阅读的SDWebImage的版本为4.3.3. 这个类提供了四个方法,这四个方法可分为两类,一类是动图处理,一类是图像方向处理. 1.私有函数 先来看一下这个类里的两个函数 /**这个函数是计算 ...
- mybatis源码阅读
说下mybatis执行一个sql语句的流程 执行语句,事务等SqlSession都交给了excutor,excutor又委托给statementHandler SimpleExecutor:每执行一次 ...
- 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment
24 UsageEnvironment使用环境抽象基类--Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类--Live555源码阅读 ...
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- 源码阅读笔记 BiLSTM+CRF做NER任务 流程图
源码阅读笔记 BiLSTM+CRF做NER任务(二) 源码地址:https://github.com/ZhixiuYe/NER-pytorch 本篇正式进入源码的阅读,按照流程顺序,一一解剖. 一.流 ...
- 源码阅读:AFNetworking(八)——AFAutoPurgingImageCache
该文章阅读的AFNetworking的版本为3.2.0. AFAutoPurgingImageCache该类是用来管理内存中图片的缓存. 1.接口文件 1.1.AFImageCache协议 这个协议定 ...
最新文章
- Java 8 类型转换及改进
- dnsmasq详解手册
- 数据挖掘流程(二):数据预处理
- 1024程序员节来啦!!.NET技术圈独家优惠劵,折后再折,赶紧来抢啊
- 20155202 实验四 Android开发基础
- Linux 应用---make及makefile的编写
- win10文件显示后缀名_Win10一开机,内存占用竟高达60%?你可以尝试这样做
- 为html.EditorFor添加样式
- SQL前三章知识点测试
- 807. 保持城市天际线
- mysql 当前用户连接数,查看mysql当前连接数的方法详解
- 1051. Pop Sequence (25)
- hook 微信公众号html,Xposed实时获取微信公众号推送
- 用java求素数饼放在数组中_【Java算法题】打印沙漏、素数对猜想、数组元素右移、双倍数、洗牌机...
- 回答完数据库连接池原理,面试官跪着求我入职他们公司
- 云班课python答案_云班课测试题答案公众号
- 遥感SCI期刊模板下载教程———IEEE TGRS、GRSL、JSTARS
- 有意思的张飞日记-_-
- QQ用户这两个文件夹要定时清理
- 一口“臊子面”的背后,是西安小吃产业发展的缩影
热门文章
- IPv6 路由信息查看命令
- CCNA(十五)思科ACL、NAT配置命令
- 20221117 今天的世界发生了什么
- 音频频谱显示-基于fmod设计音乐播放器并动态显示频谱图(二)
- 【学生网页设计作品 】关于HTML公益主题网页设计——关爱空巢老人 5页 带表单
- QTableWidget使用方法详细介绍
- 1388. 3n 块披萨
- SpringCloud微服务实战——搭建企业级开发框架(二十七):集成多数据源+Seata分布式事务+读写分离+分库分表
- leetcode: 每个元音包含偶数次的最长字符串(前缀和 + 状态压缩(位运算、hash优化))*
- 微信小程序 报错一大串 define is not defined