github 主页
readme原文:

Web Bench is a very simple tool for benchmarking WWW or proxy servers. Uses fork() for simulating multiple clients and can use HTTP/0.9-HTTP/1.1 requests. This benchmark is not very realistic, but it can test if your HTTPD can really handle so many clients at once without taking your machine down.

readme翻译:

Web Bench是一个非常简单的对WWW或代理服务器进行基准测试的工具。使用fork()模拟多个客户端,可以使用HTTP/0.9-HTTP/1.1请求。这个基准测试不是很现实,但是它可以测试HTTPD是否真的可以在不关闭机器的情况下同时处理这么多客户机。


写完http server后,逃不掉的一个问题就是性能,我使用了webbench进行测压
顺带看了看webbench的源码,做以下记录:

webbench主要有两个文件:

socket.c

提供了一个函数 Socket(char *host, int clientPort),创建socket,并建立连接,成功返回socket fd,失败返回 -1。
源码及注释:

// 创建socket,并连接服务端,成功返回socket fd,失败返回 -1
int Socket(const char *host, int clientPort) {int sock;unsigned long inaddr;struct sockaddr_in ad;struct hostent *hp;memset(&ad, 0, sizeof(ad));ad.sin_family = AF_INET;inaddr = inet_addr(host);if (inaddr != INADDR_NONE)memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));else {hp = gethostbyname(host);if (hp == NULL) return -1;memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);}ad.sin_port = htons(clientPort);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;
}

webbchch.c

提供了四个函数:

int main(int argc, char *argv[])

  • 通过 getopt_long() 函数,进行参数解析
  • 调用 build_request() 函数,创建请求
  • 调用 bench() 函数,运行程序主体
    getopt() 函数资料

代码及注释:

#include <getopt.h>
#include <rpc/types.h>
#include <signal.h>
#include <strings.h>
#include <sys/param.h>
#include <time.h>
#include <unistd.h>#include "socket.c"/* 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;}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;}//  默认 1 client,60 timeif (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.1)");break;}printf("\n");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)

根据 url 和 主函数中解析的数据,创建对应的 HTTP请求

代码及注释:


// 创建 http 请求
void build_request(const char *url) {char tmp[10];int i;bzero(host, MAXHOSTNAMELEN);bzero(request, 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;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;}// request:// GETstrcat(request, " ");// request:// GET+' '// 判断 url 是否合法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)) {fprintf(stderr,"\nOnly HTTP protocol is directly supported, set --proxy for ""others.\n");exit(2);}/* protocol/host delimiter */i = strstr(url, "://") - url + 3;/* printf("%d\n",i); */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);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);}// request:// GET 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");}// request:// GET url+'/' HTTP/1.0if (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("Req=%s\n",request);// request:// GET url+'/'// Connection: close// \r\n
}

int bench(void)

程序主体

  • 首先,测试 url、port 能够正确建立 socket
  • 创建管道 mypipe,用于父子进程通信
  • fork 创建 clients 个子进程,每个子进程执行 benchcore() 函数,并将 speed, failed, bytes 写到管道里
  • 父进程统计所有子进程的 speed, failed, bytes,并计算出最终结果

代码及注释:


/* 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);/* 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();*/// 创建 client 个 子进程/* 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);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);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)

子进程主体

  • 首先,设置信号处理函数(执行exit()),设置定时信号(alarm()
  • 死循环 goto,每次创建新socket,发送HTTP请求,读取返回消息,同时更新该子进程的 speed, failed, bytes 信息

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;// 设置定时任务:exit()if (sigaction(SIGALRM, &sa, NULL)) exit(3);// 设置定时信号,benchtime 后执行alarm(benchtime);rlen = strlen(req);// goto语句死循环
nexttry:while (1) {if (timerexpired) {if (failed > 0) {/* fprintf(stderr,"Correcting failed by signal\n"); */failed--;}return;}// 创建 sockets = 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;} else if (i == 0)break;elsebytes += i;}}if (close(s)) {failed++;continue;}speed++;}
}

webbench1.5 源码分析相关推荐

  1. 【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析

    目录[阅读时间:约10分钟] 一.概述 二.对比: gorilla/mux与net/http DefaultServeMux 三.简单使用 四.源码简析 1.NewRouter函数 2.HandleF ...

  2. SpringBoot-web开发(四): SpringMVC的拓展、接管(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) SpringBoot-web开发(二): 页面和图标定制(源码分析) SpringBo ...

  3. SpringBoot-web开发(二): 页面和图标定制(源码分析)

    [SpringBoot-web系列]前文: SpringBoot-web开发(一): 静态资源的导入(源码分析) 目录 一.首页 1. 源码分析 2. 访问首页测试 二.动态页面 1. 动态资源目录t ...

  4. SpringBoot-web开发(一): 静态资源的导入(源码分析)

    目录 方式一:通过WebJars 1. 什么是webjars? 2. webjars的使用 3. webjars结构 4. 解析源码 5. 测试访问 方式二:放入静态资源目录 1. 源码分析 2. 测 ...

  5. Yolov3Yolov4网络结构与源码分析

    Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 论文汇总 ...

  6. ViewGroup的Touch事件分发(源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,View的touch事件分发相对比较简单,可参考 View的Touch事件分发(一.初步了解) View的Touch事 ...

  7. View的Touch事件分发(二.源码分析)

    Android中Touch事件的分发又分为View和ViewGroup的事件分发,先来看简单的View的touch事件分发. 主要分析View的dispatchTouchEvent()方法和onTou ...

  8. MyBatis原理分析之四:一次SQL查询的源码分析

    上回我们讲到Mybatis加载相关的配置文件进行初始化,这回我们讲一下一次SQL查询怎么进行的. 准备工作 Mybatis完成一次SQL查询需要使用的代码如下: Java代码   String res ...

  9. [转]slf4j + log4j原理实现及源码分析

    slf4j + log4j原理实现及源码分析 转载于:https://www.cnblogs.com/jasonzeng888/p/6051080.html

最新文章

  1. 抓包工具 tcpdump 用法说明
  2. 项目导入时报错:The import javax.servlet.http.HttpServletRequest cannot be resolved
  3. php将多个页面写在一个页面,php – 将多个标签添加到WooCommerce单个产品页面
  4. STM32中使用静态“字符串的方式”
  5. oracle 表更新表,Oracle 更新表(另一张表)
  6. assert和if的使用
  7. 计算机软件复用意义何在,2009计算机科学技术导论复习要点.pdf
  8. 过滤 php 网址,php过滤html中的其他网站链接的方法(域名白名单功能)
  9. [Python] Ubuntu 16.04 上安装 python3.7 和 pip 并配置虚拟环境
  10. (转)刘巍然-关于公钥与私钥
  11. 让我们手动计算:深入研究Logistic回归
  12. POJ 3273 Monthly Expense【二分】
  13. 偷窥JCache API(JSR 107)
  14. msdia80.dll文件出现在磁盘根目录下的解决方案
  15. cmos逻辑门传输延迟时间_边沿触发器 || D触发器 || JK触发器 || 逻辑功能转换 工作特性 || 重难点 || 数电...
  16. 计算机快捷键大全截图,电脑截图快捷键是哪个?电脑快捷键使用大全
  17. OpenGL核心技术之法线贴图
  18. 草根程序员转型做项目管理走过的点点滴滴之_华为裁员感想
  19. mongodb 副本集测试
  20. Java输入某年某月某日,判断这一天是这一年的第几天?

热门文章

  1. Linux输入命令后出现内容过多,一页放不下的解决方法!
  2. 鲁棒的实时人脸检测:Robust Real-Time Face Detection
  3. CentOS7+安装Docker,并部署为知笔记服务端Docker镜像
  4. 全球将建设覆盖中国的物流专线通道网络
  5. Failure [-26: Package sdkemo new target SDK 18 doesn't support runtime permissions but the old t...
  6. python十进制小数转二进制_关于十进制小数转二进制的入门教程
  7. 51nod 1925 + 51nod 1095
  8. minix3下安装libevent
  9. 没有背水一战,腾讯忽然又有梦想了?!
  10. Android studio连接不了海马玩 的解决方法之一