webbench是用c语言来实现的网站压力测试工具,可以并发产生3万个链接测试网站。在学习webbench时候最好先简单了解一下http协议,推荐小日本的一本书《图解http》,适合入门,图文并茂,先简单介绍下wenbench的使用方法,在ubuntu14.04上安装完后,执行:
root@zhuzhu:webbench-1.5# webbench webbench [option]... URL -f|--force Don‘t wait for reply from server. -r|--reload Send reload request - Pragma: no-cache. -t|--time <sec> Run benchmark for <sec> seconds. Default 30. -p|--proxy <server:port> Use proxy server for request. -c|--clients <n> Run <n> HTTP clients at once. Default one. -9|--http09 Use HTTP/0.9 style requests. -1|--http10 Use HTTP/1.0 protocol. -2|--http11 Use HTTP/1.1 protocol. --get Use GET request method. --head Use HEAD request method. --options Use OPTIONS request method. --trace Use TRACE request method. -?|-h|--help This information. -V|--version Display program version. root@zhuzhu:webbench-1.5#
可以看到全部的参数,常用的参数 -c 表示模拟的用户数,-t 表示持续发起连接的时间:
webbench -c 5 -t 10 http://www.baidu.com/
其结果如下:
root@zhuzhu:webbench-1.5# ./webbench -c 5 -t 10 http://www.baidu.com/ Webbench - Simple Web Benchmark 1.5 Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software. Benchmarking: GET http://www.baidu.com/ 5 clients, running 10 sec. Speed=594 pages/min, 1036972 bytes/sec. Requests: 99 susceed, 0 failed.
下面开始介绍下源码,对webbench做了一些简单的修改,但基本内容和组织没有变。全部源码见:
https://github.com/zhukunbo/webbench
1、软件流程
2、代码详解
#include "socket.c" #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <rpc/types.h> #include <getopt.h> #include <strings.h> #include <time.h> #include <signal.h> #include "webbench.h" static int mypipe[2]; static int force; static int force_reload; static int http10 = 1; static int bench_time; static int proxy_port = HTTP_DEF_PORT; static int clients; static int speed; static int failed; static int bytes; static int time_out; static char *proxy_host; static int method = METHOD_GET; static char host[MAXHOSTNAMELEN]; static char request[REQUEST_SIZE]; int debug_open = 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}, {"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} }; 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" ); } static void build_requst(const char *url) { int i; char tmp[16]; bzero(host, MAXHOSTNAMELEN); bzero(request, REQUEST_SIZE); if (force_reload && (proxy_host != 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; } /* 客户端发起的请求类型,默认为GET,请求资源 */ switch (method){ 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 (strstr(url, "://") == NULL) { fprintf(stderr, "\n %s is no a vaild url. \n",url); exit(EXIT_FAILURE); } if (strlen(url) > 1500) { fprintf(stderr, "\n URL is too long \n"); exit(EXIT_FAILURE); } if (proxy_host == NULL) { if (strncasecmp("http://", url, 7) != 0) { fprintf(stderr, "\nOnly HTTP protocol is directly supported, set --proxy for others.\n"); exit(EXIT_FAILURE); } } i = strstr(url, "://") - url + 3; if (strstr(url + i, "/") == NULL) { fprintf(stderr,"\nInvalid URL syntax - hostname don‘t ends with ‘/‘.\n"); exit(EXIT_FAILURE); } if (proxy_host == NULL) { if ((index(url + i, ‘:‘) != NULL) && (index(url + i, ‘:‘) < index(url + i, ‘/‘))) { strncpy(host, url + i, strchr(url + i, ‘:‘) - url - i); bzero(tmp, sizeof(tmp)); strncpy(tmp, index(url + i, ‘:‘) + 1, strchr(url + i, ‘/‘) - index(url + i, ‘:‘) - 1); proxy_port = atoi(tmp); if (proxy_port == 0) { proxy_port = HTTP_DEF_PORT; } } else { strncpy(host, url + i, strcspn(url + i, "/")); } PRINT_DEG("host is %s, proxy_port = %d", host, proxy_port); strcat(request + strlen(request), url + i + strcspn(url + i, "/")); } else { strcat(request, url); } PRINT_DEG("the requst is %s \n", request); 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 "PRG_VERSION"\r\n"); } if ((proxy_host == NULL) && (http10 > 0)) { strcat(request,"Host: "); strcat(request,host); strcat(request,"\r\n"); } if (force_reload && (proxy_host != NULL)) { strcat(request,"Pragma: no-cache\r\n"); } if (http10 > 1) { strcat(request,"Connection: close\r\n"); } /* add empty line in the end */ if (http10 > 0) { strcat(request,"\r\n"); } } static void alarm_handler(int signal) { PRINT_DEG("it is time out"); time_out = 1; } static void bench_calc(const char *host,const int port,const char *req) { int len, n; int sock_id; char buff[1500]; struct sigaction sa; /* setup alarm signal handler */ sa.sa_handler = alarm_handler; sa.sa_flags=0; if (sigaction(SIGALRM, &sa, NULL)) { exit(3); } alarm(bench_time); len = strlen(req); re_try: while (1) { if (time_out) { if (failed > 0) { --failed; } return; } sock_id = create_socket_info(host, port); if (sock_id < 0) { failed++; continue; } if (len != write(sock_id, req, len)) { failed++; close(sock_id); continue; } if (http10 == 0) { if (shutdown(sock_id, 1)) { failed++; close(sock_id); continue; } } if (force == 0) { /* 若是强制则不要等待服务器相应 */ while (1) { if (time_out) { break; } n = read(sock_id, buff, 1500); if (n < 0) { failed++; close(sock_id); goto re_try; } else if (n == 0) { break; } else { bytes += n; } } } if (close(sock_id)) { failed++; continue; } speed++; } } static int core_process(void) { int i; int n, m, k; pid_t pid; FILE *pipe_fd; /* 创建管道 */ if (pipe(mypipe)) { perror("pipe failed."); return -1; } /* fork process number of clients */ for (i = 0; i < clients; i++) { /* 创建线程 */ pid = fork(); if (pid <= (pid_t)0) { sleep(1); /* 让父进程先运行,防止子进程先运行后,写管道失败 */ break; } } if (pid < (pid_t)0) { fprintf(stderr,"problems forking worker no. %d\n",i); perror("fork failed."); return -1; } if (pid == 0) { /* this is a child process */ bench_calc(((proxy_host == NULL) ? host: proxy_host), proxy_port, request); /* 向管道中写统计好的数据 */ pipe_fd = fdopen(mypipe[1], "w"); if (pipe_fd == NULL) { perror("open pipe for writing failed."); return -1; } PRINT_DEG("speed, failed, bytes= %d %d %d", speed, failed, bytes); fprintf(pipe_fd, "%d %d %d\n", speed, failed, bytes); fclose(pipe_fd); return 0; } else { PRINT_DEG("this is father"); pipe_fd = fdopen(mypipe[0], "r"); if (pipe_fd == NULL) { perror("open pipe for reading failed."); return -1; } setvbuf(pipe_fd, NULL, _IONBF, 0); speed = 0; failed = 0; bytes = 0; PRINT_DEG("speed, failed, bytes= %d %d %d", speed, failed, bytes); while (1) { /* 读管道 */ pid = fscanf(pipe_fd, "%d %d %d", &m, &n, &k); if (pid < 2) { fprintf(stderr,"Some of our childrens died.\n"); break; } speed += m; failed += n; bytes += k; if (--clients == 0) { break; } PRINT_DEG("clients = %d", clients); } fclose(pipe_fd); printf("\nSpeed=%d pages/min, %d bytes/sec.\nRequests: %d susceed, %d failed.\n", (int)((speed + failed) / (bench_time / 60.0f)), (int)(bytes / (float)bench_time), speed, failed); } return speed; } int main(int argc, char *argv[]) { int opt; int options_index=0; int socket_fd; char *tmp=NULL; /*1、参数检查 */ if (argc == 1) { usage(); exit(EXIT_FAILURE); } /*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(PRG_VERSION); exit(EXIT_SUCCESS); case ‘t‘: bench_time = atoi(optarg); break; case ‘p‘: /* 获取代理服务器端口号,strrchr,返回从左边开始最后一个比配的字符以后的字符串 */ tmp = strrchr(optarg, ‘:‘); proxy_host = optarg; if (tmp == NULL) { break; } if (tmp == optarg) { fprintf(stderr, "Error in option --proxy %s: Missing hostname.\n", optarg); exit(EXIT_FAILURE); } if (tmp == (optarg + strlen(optarg) - 1)) { fprintf(stderr, "Error in option --proxy %s Port number is missing.\n", optarg); exit(EXIT_FAILURE); } proxy_port = atoi(tmp + 1); break; case ‘ ‘ : case ‘?‘: case ‘h‘: usage(); exit(EXIT_FAILURE); case ‘c‘: clients = atoi(optarg); break; default : usage(); exit(EXIT_FAILURE); } } if (optind == argc) { fprintf(stderr, "webbench: Missing URL!\n"); usage(); exit(EXIT_FAILURE); } /* 设置默认的参数 */ if (clients == 0) { clients = 1; /* 默认模拟一个用户 */ } if (bench_time == 0) { bench_time = 30; /* 默认发起连接时间为30秒 */ } /********************** info print *************************/ fprintf(stderr, "Webbench - Simple Web Benchmark "PRG_VERSION"\n" "Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\n" ); build_requst(argv[optind]); /* http协议参数相关参数设置 */ 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"); printf("%d clients \n", clients); printf("running %d sec", bench_time); if (force) { printf(", early socket close"); } if (proxy_host != NULL) { printf(", via proxy server %s:%d",proxy_host,proxy_port); } if (force_reload) { printf(", forcing reload"); } printf(".\n"); /********************** info print end*************************/ /*3、check avaibility of target server */ socket_fd = create_socket_info((proxy_host == NULL) ? host : proxy_host, proxy_port); /* 创建套接字,主要用来测试服务器可用 */ if (socket_fd < 0) { fprintf(stderr,"\nConnect to server failed. Aborting benchmark.\n"); exit(EXIT_FAILURE); } close(socket_fd); /*4、核心处理 */ return core_process(); }
/* * host :the host addr * client_port :the dest port num * */ int create_socket_info(const char *host, int cli_port) { int sock; unsigned long inaddr; struct hostent *hp; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); inaddr = inet_addr(host); if (inaddr != INADDR_NONE) { memcpy(&addr.sin_addr, &inaddr, sizeof(inaddr)); } else { hp = gethostbyname(host); if (hp == NULL) { return -1; } memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); } addr.sin_port = htons(cli_port); addr.sin_family = AF_INET; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { return sock; } /* connect to the server */ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { return -1; } return sock; }
本文出自 “12128867” 博客,请务必保留此出处http://12138867.blog.51cto.com/12128867/1918475
原文地址:http://12138867.blog.51cto.com/12128867/1918475