WebBench压力测试工具(详细源码注释+分析)
WebBench压力测试工具(详细源码注释+分析)
本文适合人群:对WebBench实现感兴趣的人
WebBench原理:
Linux下使用的服务器压力测试工具,利用fork建立多个子进程,每个子进程在测试时间内不断发送请求报文,建立多个连接,然后由父进程统计:TCP连接成功次数,TCP连接失败次数,从服务器接收的数据量
WebBench适用于小,中型网站的服务器压力测试(对淘宝,百度这种大型网站不存在测压作用)
WebBench支持的并行连接数:32768
进程号pid是short类型的,short类型最大为32768
所以WebBench最多可以模拟3万多个并发连接去测试网站的负载能力
WebBench源码理解坑点:
1.clients参数
//创建子进程进行测试,子进程数量和clients有关for(i=0; i<clients; i++){// pid 为 pid_t 类型 表示进程号 pid=fork();//建立子进程//fork失败 子进程错误if(pid <= (pid_t) 0){sleep(1); //当前进程挂起1毫秒,将cpu时间交给其他进程break; //跳出去,阻止子进程继续fork }}
子进程数量=1+2+3+……+(clients)
关键是的fork函数的理解:fork一个子进程,该子进程将要执行的指令和父进程继续执行的指令是一模一样的
2.benchtime参数
一个子进程在benchtime时间内,不断发送http请求,建立多个连接进行测试,到达benchtime时间则停止测试,返回测试结果(连接成功次数,连接失败次数,服务器响应内容字节数)
针对原版的WebBench所作的改进:
1.弃用了TRACE请求方法:回显服务器收到的请求
因为一般服务器都不支持这个方法,支持这个方法的服务器存在跨站脚本漏洞,攻击者可以此漏洞欺骗合法用户并得到他们的私人信息
2.增加了连接失败类型的统计,结果更加直观
一共两个文件socket.c和webbench.c
加上注释,代码不超过一千行
sorcket.c:
#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>/*sockaddr_in分析:#include <netinet/in.h>和#include <arpa/inet.h>定义的struct sockaddr {__SOCKADDR_COMMON (sa_); //协议族char sa_data[14]; //地址+端口号 };sockaddr缺陷:把目标地址和端口号混在一起了 而sockaddr_in就解决了这一缺陷 将端口号和IP地址分开存储struct sockaddr_in {sa_family_t sin_family; //地址族uint16_t sin_port; //16位TCP/UDP端口号struct in_addr sin_addr; //32位IP地址char sin_zero[8]; //不使用,只为了内存对齐 };*//*hostent分析: host entry的缩写 记录主机信息包括主机名,别名,地址类型,地址长度和地址列表struct hostent {char *h_name; //正式主机名char **h_aliases; //主机别名int h_addrtype; //主机IP地址类型:IPV4-AF_INETint h_length; //主机IP地址字节长度,对于IPv4是四字节,即32位char **h_addr_list; //主机的IP地址列表}; #define h_addr h_addr_list[0] //保存的是IP地址主机的的地址是列表形式的原因: 当一个主机又多个网络接口时,自然有多个地址*///host ip地址或者主机名 //clientPort 端口 int Socket(const char *host, int clientPort) {int sock;unsigned long inaddr;struct sockaddr_in ad;//地址信息struct hostent *hp;//主机信息/*因为host可能是ip地址或者主机名所以当host为主机名的时候需要通过主机名得到IP地址*///初始化地址memset(&ad, 0, sizeof(ad));//采用TCP/IP协议族ad.sin_family = AF_INET;//点分十进制IP转化为二进制IPinaddr = inet_addr(host);//输入为IP地址if (inaddr != INADDR_NONE)//将IP地址复制给ad的sin_addr属性memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));//输入不是IP地址,是主机名else{//通过主机名得到主机信息hp = gethostbyname(host);//没有得到主机信息if (hp == NULL)return -1;//将IP地址复制给ad的sin_addr属性memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);}/*将端口号从主机字节顺序变成网络字节顺序就是整数在地址空间存储方式变为高字节存放在内存低字节处网络字节顺序是TCP/IP中规定好的一种数据表示格式,与CPU和操作系统无关从而可以保证数据在不同主机之间传输时能够被正确解释网络字节顺序采用大尾顺序:高字节存储在内存低字节处*/ad.sin_port = htons(clientPort);/*AF_INET: IPV4网络协议SOCK_STRAM: 提供面向连接的稳定数据传输,即TCP协议*///创建一个采用IPV4和TCP的socketsock = socket(AF_INET, SOCK_STREAM, 0);//创建socket失败if (sock < 0)return sock;//建立连接 连接失败返回-1if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)return -1;//创建成功 返回socketreturn sock; }
webbench.c
#include "socket.c" #include <unistd.h> #include<stdio.h> #include <sys/param.h> #include <rpc/types.h> #include <getopt.h> #include <strings.h> #include <time.h> #include <signal.h> #include<string.h> #include<error.h>//用法和各参数的详细意义 static void usage(void) {fprintf(stderr,"webbench [parameter]... URL\n"" -f|--force No waiting for server response \n"" -r|--reload Re-request loading (no caching) \n"" -t|--time <sec> Set run time in seconds, default 30 seconds \n"" -p|--proxy <server:port> Setting the number of proxy servers \n"" -c|--clients <n> How many clients are created, default is 1 \n"" -9|--http09 Using HTTP 0.9 protocol \n"" -1|--http10 Using HTTP 1.0 protocol \n"" -2|--http11 Using HTTP 1.1 protocol \n"" -G|--get Using GET request method \n"" -H|--head Using HEAD request method \n"" -O|--options Using OPTIONS request method \n"" -?|-h|--help Display help information \n"" -V|--version Display program version information \n" ); };//支持的http请求方法 #define METHOD_GET 0 #define METHOD_HEAD 1 #define METHOD_OPTIONS 2 #define METHOD_TRACE 3//默认参数设置,一般需要自己传入命令行参数设置 int method=METHOD_GET; //默认请求方法为get int clients=1; //默认只模拟一个客户端 int force=0; //默认需要等待服务器响应 int force_reload=0; //失败时重新请求 int proxyport=80; //默认访问服务器端口为80 char *proxyhost=NULL; //默认无代理服务器 int benchtime=30; //默认模拟请求时间为30s//支持的http版本号 int http10=1; /* 0表示http0.9 1表示http1.0 2表示http1.1 *//* 内部 */ int mypipe[2]; //管道用于父子进程通信 char host[MAXHOSTNAMELEN]; //存储服务器网络地址 #define REQUEST_SIZE 2048 //最大请求次数 char request[REQUEST_SIZE]; //存放http请求报文信息数组//判断测试时长是否已经到达设定时间 volatile int timeout=0; /*volatile:类型修饰符,作为指令关键字,确保本指令不会因为编译器优化而省略且每次要求重新读值,编译器在用到这个变量的时候都必须小心的重新读取这个变量的值,而不是使用保存在寄存器里的备份,保证每次读到的都是最新的*///测试结果 int speed=0; //成功得到服务器响应的子进程数量 int failed=0; //没有成功得到服务器响应的子进程数量 int bytes=0; //所有子进程读取到服务器回复的总字节数int connect_failed=0; int send_failed=0; int wclose_failed=0; int read_failed=0; int sclose_failed=0;//程序版本号 #define PROGRAM_VERSION "1.5"/* 函数声明 *///子进程真正相服务器发出请求报文并以其得到此期间的相关数据 static void benchcore(const char* host,const int port, const char *req);//父进程创建子进程,读取子进程测试得到的数据,然后统计处理 static int bench(void);//构造http请求报文 static void build_request(const char *url);//闹钟信号处理函数 static void alarm_handler(int signal) {//到达设定的测压时间,则调用闹钟信号处理函数timeout=1;//timerexpired为1则会在循环中跳出测试 }//构造长选项和短选项的对应 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},{"version",no_argument,NULL,'V'},{"proxy",required_argument,NULL,'p'},{"clients",required_argument,NULL,'c'},{NULL,0,NULL,0} };int main(int argc, char *argv[]) {//argc表示参数个数//argv[0]表示自身运行的路径和程序名//argv[1]指向第1个参数//argv[n]指向第n个参数int opt=0;int options_index=0;char *tmp=NULL;//进行命令行参数的处理//1.命令行没有输入参数if(argc==1){usage();//显示提示信息return 2;}//命令行有输入参数则一个个解析//"frt:p:c:?V912"中一个字符后面加一个冒号代表该命令后面接一个参数//比如t,p,c命令,后面都要接一个参数//连续两个冒号则表示参数可有可无while((opt=getopt_long(argc,argv,"frt:p:c:?V912GHO",long_options,&options_index))!=EOF ){switch(opt){case 'f':force=1;//不等待服务器响应printf("No waiting for server response\n");break;case 'r'://重新请求加载(无缓存)force_reload=1;printf("Re-request loading (no caching)\n");break;case '9'://使用http/0.9协议来构造请求http10=0;printf("Using HTTP/0.9\n");break;case '1':http10=1;//使用http/1.0协议来构造请求printf("Using HTTP/1.0\n");break;case '2':http10=2;//使用http/1.1协议来构造请求printf("Using HTTP/1.1\n");break;case 'V':printf(PROGRAM_VERSION"\n");//显示程序版本信息exit(0);case 't'://设置运行时间,单位:秒,默认为30秒benchtime=atoi(optarg);//optarg指向选项后的参数printf("benchtime=%d\n",benchtime);break;case 'c'://创建多少个客户端,默认为1个clients=atoi(optarg);//同上printf("clients=%d\n",clients);break;case 'p'://使用代理服务器,则设置其代理网络号和端口号,格式:-p server:port//server:port是一个参数,下面把这个字符串解析成服务器地址和端口两个参数 tmp=strrchr(optarg,':');//在optagr中找到':'最后出现的位置 proxyhost=optarg;if(tmp==NULL)//没有端口号 {break;}if(tmp==optarg)//端口号在optarg最开头,说明缺失主机地址 {fprintf(stderr,"Option parameter error,Proxy server %s: Missing host name ",optarg);return 2;}if(tmp==optarg+strlen(optarg)-1)//':'在最末尾,说明缺失端口号 {fprintf(stderr,"Option parameter error,Proxy server %s: Missing port number ",optarg);return 2;}*tmp='\0';//将optarg从':'开始截断,前面就是主机名,后面是端口号 proxyport=atoi(tmp+1);//设置代理服务器端口号 printf("Using proxy server %s:%d\n",proxyhost,proxyport);break;case 'G':method=METHOD_GET;printf("Using GET request method \n");break;case 'H':method=METHOD_HEAD;printf("Using HEAD request method \n");break;case 'O':method=METHOD_OPTIONS;printf("Using OPTIONS request method \n");break;case '?'://显示帮助信息 usage();return 2;break;default://失败也显示帮助信息 usage();return 2;break;}}//命令参数解析完毕之后,刚好是读到URL,此时argv[optind]指向URL//URL参数为空if(optind==argc){fprintf(stderr,"Missing URL\n");usage();return 2;}//设置默认值if(clients==0)clients=1;if(benchtime==0)benchtime=30;//程序说明fprintf(stderr,"WebBench: A Lightweight Web Pressure Measuring Tool "PROGRAM_VERSION" covered by YB \nGPL Open Source Software\n");//构造请求报文build_request(argv[optind]);//参数为URL//请求报文构造好了,开始测压printf("\nIn testing :\n");//选择请求方法switch(method){case METHOD_OPTIONS:printf("OPTIONS");break;case METHOD_HEAD:printf("HEAD");break;case METHOD_GET:printf("GET");break;default:printf("GET");break;}//打印URLprintf(" %s",argv[optind]);switch(http10){case 0:printf("(Using HTTP/0.9)");break;case 1:printf("(Using HTTP/1.0)");break;case 2:printf("(Using HTTP/1.1)");break;}printf("\n");printf("Operation parameters :\n");printf("%d Clients",clients);printf(",Testing running %d s",benchtime);if(force)printf(",Choose to close the connection ahead of time ");if(proxyhost!=NULL)printf(",Through proxy server %s:%d ",proxyhost,proxyport);if(force_reload)printf(",Choose no cache ");/**换行不能少!库函数是默认行缓冲,子进程会复制整个缓冲区*若不换行刷新缓冲区,子进程会把缓冲区的也打出来*而换行后缓冲区就刷新了*子进程的标准库函数的那块缓冲区就不会有前面这些了*/printf(".\n");//真正开始压力测试!return bench(); }//父进程创建子进程,读子进程测试到的数据,然后统计处理 static int bench(void) {int i,j;int k;int c1,c2,c3,c4,c5;pid_t pid=0;//进程号定义 实际上也是int型的FILE *f;//文件//先检查一下目标服务器是可用性i=Socket(proxyhost==NULL?host:proxyhost,proxyport);//目标服务器不可用if(i<0){fprintf(stderr,"\n Connection server failed, interrupt test \n");return 3;}//尝试连接成功了,关闭连接 close(i);//建立父子进程通信的管道if(pipe(mypipe)){perror(" Communication Pipeline Failure ");return 3;}/*父进程创建子进程后,fork函数是让子进程完全拷贝父进程,包括父进程上下文,什么意思呢?就是说父进程的EIP(CPU的下一条指令地址)以及变量等等一律拷贝,也就是说,父进程执行过的代码子进程是不会再执行,子进程下一条该执行的命令与父进程完全一样!!!*///创建子进程进行测试,子进程数量和clients有关for(i=0; i<clients; i++){// pid 为 pid_t 类型 表示进程号 pid=fork();//建立子进程//fork失败 子进程错误if(pid <= (pid_t) 0){sleep(1); //当前进程挂起1毫秒,将cpu时间交给其他进程break; //跳出去,阻止子进程继续fork }}//处理fork失败情况if( pid < (pid_t) 0){fprintf(stderr,"The %d Subprocess creation failed ",i);perror(" Failure to create subprocesses ");return 3;}//当前进程是子进程if(pid == (pid_t) 0){//由子进程发出请求报文 根据是否采用代理发送不同的报文if(proxyhost==NULL)benchcore(host,proxyport,request);elsebenchcore(proxyhost,proxyport,request);//子进程获得管道写端的文件指针,准备向父进程写结果f=fdopen(mypipe[1],"w");//管道写端打开失败if(f==NULL){perror(" Pipeline Writer End Failed to Open ");return 3;}/*向管道中写入该孩子进程在一定时间内请求成功的次数失败次数读取到服务器回复的总字节数*/fprintf(f,"%d %d %d %d %d %d %d %d\n",speed,failed,bytes,connect_failed,send_failed,wclose_failed,read_failed,sclose_failed);//关闭写端 fclose(f);return 0;}//当前进程是父进程else{//父进程获得管道读端的文件指针f=fdopen(mypipe[0],"r");//管道读端打开失败if(f==NULL){perror(" Pipeline Reader Failed to Open ");return 3;}/*fopen标准IO函数是自带缓冲区的我们输入的数据非常短,并且数据要及时所以没有缓冲是最合适的我们不需要缓冲区因此把缓冲类型设置为_IONBF*/setvbuf(f,NULL,_IONBF,0);speed=0; //连接成功次数,后面除以时间可以得到速度failed=0; //失败的请求次数bytes=0; //服务器回复的总字节数 connect_failed=0;send_failed=0;wclose_failed=0;read_failed=0;sclose_failed=0;//父进程不停的读while(1){//读入参数以及得到成功得到的参数的个数pid=fscanf(f,"%d %d %d %d %d %d %d %d",&i,&j,&k,&c1,&c2,&c3,&c4,&c5);//成功得到的参数个数小于8if(pid<8){fprintf(stderr,"A child process deaid\n");break;}//计总数speed+=i;failed+=j;bytes+=k;connect_failed+=c1;send_failed+=c2;wclose_failed+=c3;read_failed+=c4;sclose_failed+=c5;if(--clients==0)//记录已经读了多少个子进程的数据,读完就退出break;}//关闭读端 fclose(f);//统计处理结果printf("\n Speed:%d pages/min,%lld bytes/s.\nRequest:%d Success,%d Fail\n",\(int)((speed+failed)/(benchtime/60.0f)),\(int)(bytes/(float)benchtime),\speed,failed);//失败的类型及个数printf("Reasons for failure:\n");printf("connect failed:%d\n",connect_failed);printf("send message failed:%d\n",send_failed);printf("write-side shutdown failed:%d\n",wclose_failed);printf("read server message failed:%d\n",read_failed);printf("socket close failed:%d\n",sclose_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;//信号处理函数定义//设置alarm_handler函数为闹钟信号处理函数sa.sa_handler=alarm_handler;sa.sa_flags=0;if(sigaction(SIGALRM,&sa,NULL))//超时会产生信号SIGALRM,用sa中指定函数处理exit(3);alarm(benchtime);//开始计时 rlen=strlen(req);//得到请求报文的长度 nexttry:while(1){//只有在收到闹钟信号后会使得timeout=1if(timeout)//超时返回 {//修正失败信号if(failed>0)failed--;if(connect_failed>0)connect_failed--;else if(send_failed>0)send_failed--;else if(wclose_failed>0)wclose_failed--;else if(read_failed>0)read_failed--;else if(sclose_failed>0)sclose_failed--;return;}//建立到目的网站的tcp连接,发送http请求s=Socket(host,port);//连接失败if(s<0){failed++;//失败次数+1connect_failed++;continue;}//发出请求报文if(rlen!=write(s,req,rlen))//write函数会返回实际写入的字节数 {failed++;//实际写入的字节数和请求报文字节数不相同,写失败,发送1失败次数+1send_failed++;close(s);//写失败了也不要忘记关闭套接字continue;}//http/0.9的特殊处理/**因为http/0.9是在服务器回复后自动断开连接*在此可以提前先彻底关闭套接字的写的一半,如果失败了那肯定是个不正常的状态*事实上,关闭写后,服务器没有写完数据也不会再写了,这个就不考虑了*如果关闭成功则继续往后,因为可能还需要接收服务器回复的内容*当这个写一定是可以关闭的,因为客户端也不需要写,只需要读*因此,我们主动破坏套接字的写,但这不是关闭套接字,关闭还是得用close*/if(http10==0){if(shutdown(s,1))//1表示关闭写 关闭成功返回0,出错返回-1 {failed++;//关闭出错,失败次数+1wclose_failed++;close(s);//关闭套接字continue;}}//foece=0 默认需要等待服务器回复if(force==0){//从套接字读取所有服务器回复的数据while(1){//超时标志为1,不再读取服务器回复的数据if(timeout)break;//读取套接字中1500个字节数据到buf数组中i=read(s,buf,1500);//如果套接字中数据小于要读取的字节数1500会引起阻塞 返回-1//read返回值://未读取任何数据 返回 0//读取成功 返回 已经读取的字节数//阻塞 返回 -1//读取阻塞了if(i<0){failed++; //失败次数+1read_failed++;close(s); //关闭套接字,不然失败次数多会严重浪费资源goto nexttry; //这次失败了那么继续请求下一次连接和发出请求 }//读取成功else{if(i==0)break;//没有读取到任何字节数elsebytes+=i;//从服务器读取到的总字节数增加 }}}/*close返回返回值成功 返回 0失败 返回 -1*///套接字关闭失败if(close(s)){failed++;//没有成功得到服务器响应的子进程数量sclose_failed++;continue;}//套接字关闭成功 成功得到服务器响应的子进程数量+1speed++;} }//构造http报文请求到request数组 /*典型的http/1.1的get请求如下:从下一行开始 GET /test.jpg HTTP/1.1 //请求行:请求方法+url+协议版本 User-Agent: WebBench 1.5 Host:192.168.10.1 Pragma: no-cache Connection: close//从上行结束,最后必须要有一个空行该函数目的就是根据需求填充出这样一个http请求放到request报文请求数组中 */ void build_request(const char *url) {//存放端口号的中间数组char tmp[10];//存放url中主机名开始的位置int i;//初始化memset(host,0,MAXHOSTNAMELEN);memset(request,0,REQUEST_SIZE);//判断应该使用的http协议//1.缓存和代理都是都是http/1.0以后才有到的if(force_reload && proxyhost!=NULL && http10<1)http10=1;//2.head请求是http/1.0后才有的if(method==METHOD_HEAD && http10<1)http10=1;//3.options请求和reace请求都是http/1.1才有if(method==METHOD_OPTIONS && http10<2)http10=2;if(method==METHOD_TRACE && http10<2)http10=2;//开始填写http请求//填充请求方法到请求行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," ");//判断url的合法性//1.url中没有 "://" 字符if(NULL==strstr(url,"://")){fprintf(stderr,"\n %s:is an illegal URL\n",url);exit(2);//结束当前进程 2表示是因为url不合法导致进程停止的 }//2.url过长if(strlen(url)>1500){fprintf(stderr,"URL too long\n");exit(2);}//3.若无代理服务器,则只支持http协议if(proxyhost==NULL){//忽略字母大小写比较前7位if (0!=strncasecmp("http://",url,7)){fprintf(stderr,"\n URL can't be parsed, need it or not, but don't choose to use proxy server\n");usage();exit(2);}}//在url中找到主机名开始的地方//比如:http://baidu.com:80///主机名开始的地方为bai....//i==7i=strstr(url,"://")-url+3;//4.从主机名开始的地方开始往后找,没有 '/' 则url非法if(strchr(url+i,'/')==NULL){fprintf(stderr,"\n URL illegal: hostname does not end with'/' \n");exit(2);}//url合法性判断到此结束//开始填写url到请求行//无代理时if(proxyhost==NULL){//存在端口号 比如http://www.baidu.com:80/if(index(url+i,':')!=NULL && index(url+i,':')<index(url+i,'/')){//填充主机名到host字符数组,比如www.baidu.comstrncpy(host,url+i,strchr(url+i,':')-url-i);//初始化存放端口号的中间数组memset(tmp,0,10);//切割得到端口号strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);/* printf("tmp=%s\n",tmp); *///设置端口号 atoi将字符串转整型proxyport=atoi(tmp);//避免写了';'却没有写端口号,这种情况下默认设置端口号为80if(proxyport==0)proxyport=80;}//不存在端口号else{//填充主机名到host字符数组,比如www.baidu.comstrncpy(host,url+i,strcspn(url+i,"/"));}// printf("Host=%s\n",host);//将主机名,以及可能存在的端口号以及请求路径填充到请求报文中//比如url为http://www.baidu.com:80/one.jpg///就是将www.baidu.com:80/one.jpg填充到请求报文中strcat(request+strlen(request),url+i+strcspn(url+i,"/"));}//存在代理服务器时就比较简单了,直接填写,不用自己处理else{// printf("ProxyHost=%s\nProxyPort=%d\n",proxyhost,proxyport);//直接将url填充到请求报文 strcat(request,url);}//填充http协议版本到请求报文的请求行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");//不存在代理服务器且http协议版本为1.0或1.1,填充Host字段//当存在代理服务器或者http协议版本为0.9时,不需要填充Host字段//因为http0.9版本没有Host字段,而代理服务器不需要Host字段if(proxyhost==NULL && http10>0){strcat(request,"Host: ");strcat(request,host);//Host字段填充的是主机名或者IPstrcat(request,"\r\n");}/*pragma是http/1.1之前版本的历史遗留问题,仅作为与http的向后兼容而定义规范定义的唯一形式:Pragma:no-cache若选择强制重新加载,则选择无缓存*/if(force_reload && proxyhost!=NULL){strcat(request,"Pragma: no-cache\r\n");}/*我们的目的是构造请求给网站,不需要传输任何内容,所以不必用长连接http/1.1默认Keep-alive(长连接)所以需要当http版本为http/1.1时要手动设置为 Connection: close*/if(http10>1)strcat(request,"Connection: close\r\n");//在末尾填入空行if(http10>0)strcat(request,"\r\n");//fprintf("\nRequest:\n%s\n",request); }
WebBench压力测试工具(详细源码注释+分析)相关推荐
- 基于stm32、0.96寸OLED实现的贪吃蛇小游戏(详细源码注释)
简介:本实验基于stm32最小系统.0.96寸OLED(68*128)和摇杆实现一个经典的贪吃蛇小游戏.项目源码地址:点击下载. 硬件设计: 普通摇杆,0.96寸OLED 单色屏幕(SPI协议通讯), ...
- 基于stm32、0.96寸OLED实现的俄罗斯方块小游戏(详细源码注释)
概述:本实验基于stm32最小系统.0.96寸OLED(68*128)和摇杆实现一个经典的俄罗斯方块小游戏.项目源码地址:点击下载. 硬件要求: 普通摇杆,两个电位器和一个开关组成,左右摇动控制一个电 ...
- 模糊测试工具AFL源码浅析
前言 AFL是一款著名的模糊测试的工具,最近在阅读AFL源码,记录一下,方便以后查阅. 环境 项目:AFL 编译项目:将编译的优化选项关闭,即改写成-O0 afl-gcc.c 使用gdb加载afl-g ...
- 测试通达信指标胜率的软件,选股指标成功率测试工具(源码 副图/预警 通达信)非常实用...
要想知道自己选出的个股,在未来几天内能否上涨,涨多少?如果有一个方便的测试工具,也许会给你的抄股带来不少的方便. 只要你的选股指标中选股符号是:XG(如果是别的选股符号,你可以将它改成"XG ...
- MorphSVGPlugin from GreenSock 的源码注释分析
不管是ActionScript(flash)还是JavaScript,GreenSock的GSAP几乎是公认的最强动画库. 常用的就是其中的TweenLite.TweenMax.TimelineLit ...
- 【Android 异步操作】Handler 机制 ( Handler 常用用法 | HandlerThread 简介 | HandlerThread 源码注释分析 )
文章目录 一.Handler 常用用法 二.HandlerThread 简介 三.HandlerThread 源码 一.Handler 常用用法 主线程 Handler 主要作用 : Looper 和 ...
- Python3爬取meizitu(详细源码+注释)
# -*- coding=utf-8 -*- ''' 人生苦短,我用Python '''import time import requests import re import os from bs4 ...
- 一篇文章看懂TPCx-BB(大数据基准测试工具)源码
TPCx-BB是大数据基准测试工具,它通过模拟零售商的30个应用场景,执行30个查询来衡量基于Hadoop的大数据系统的包括硬件和软件的性能.其中一些场景还用到了机器学习算法(聚类.线性回归等).为了 ...
- poco源码简单分析
自动化工具poco源码简单分析 Airtest简介 Airtest是网易游戏开源的一款UI自动化测试项目,目前处于公开测试阶段,该项目分为AirtestIDE.Airtest.Poco.Testlab ...
最新文章
- P4491 [HAOI2018]染色
- Android 触摸事件处理机制
- 轻量级数据库Sqlite的使用
- pyqtgraph初探
- 面试官问:断网了,还能ping通 127.0.0.1 吗?为什么?
- 杭电1492 The number of divisors(约数) about Humble Numbers
- vscode的eslint无效_VSCode配置eslint
- 格式化信息窗口内容—ArcGIS API for JavaScript
- 负频率与双边频谱(信号与系统的基本概念)
- java随机数_Java随机
- Linux下如何用GDB调试c++程序 [版本2]
- Yaksa让你抛弃Adapter和ViewHolder写RecyclerView
- 信号与系统奥本海姆第二版课后习题答案与详解(免下载,附有知识总结)
- 网络调试助手无法连接tcp服务器,S71500做TCP客户端和第三方网络调试助手做服务器无法通信...
- Linux中使用sed命令替换字符串
- 用c++语言编写的小程序,利用C++编写一些有趣的小程序
- 学生上课签到系统开发总结
- Unity之软件在win7旗舰版上无法全屏“铺满”显示
- Spark On YARN 环境搭建
- 【从零开始学习深度学习】25.卷积神经网络之LeNet模型介绍及其Pytorch实现【含完整代码】
热门文章
- 在路径没问题的情况下,组件报错Component is not found
- 认识机器学习 机器学习实战第一章
- win7系统64位系统怎么计算机配置,Win7系统电脑最低配置要求是什么?
- 【大功率摄影灯/舞台灯RGB调光驱动方案】DC-DC降压恒流LED双路调光芯片FP7126,共阳极高辉无频闪调光,调光深度可达万分之一
- 区块链应用场景架构解决方案(ppt)
- TestStand-用户界面
- Rufus 3.19 beta 引入自定义 Windows 11 安装对话框
- 你所表现的负责可能正是在逃避责任
- arranged by JerryC
- 转:网络虚拟(包括overlay、underlay介绍)