Web Bench is a Web site stress test tool. Its last update was in 2004, more than 10 years ago. Its source code total of more than 500 lines, all written in C language, up to the simulation of tens of thousands of concurrent connections.
The principle is also relatively simple, is to use fork to create a child process, through the child process to test the HTTP connection, the test results are written to the pipeline, and then the parent process to read the pipeline information to calculate the test results. Under Flowchart:
Its source code is composed of 2 files
SOCKET.C is the creation of a socket connection. The main code is in WEBBENCH.C.
There are several main functions in the WEBBENCH.C.
The static void usage (void) is an indication of how to use this program when using an error.
void Build_request (const char *url) is used to create an HTTP connection request.
static int Bench (void) creates pipelines and child processes, calling test HTTP functions.
void Benchcore (const char *host,const int port,const char *req) tests the HTTP request.
Socket.c Source code and comments:
/* $Id: SOCKET.C 1.1 1995/01/01 07:11:14 Cthuang EXP $ * * This module have been modified by Radim Kolar for OS/2 emx *//** Module:socket.c program:popclient SC CS 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>/***********: Establish network connection via address and port @host: Network address @clientport: Port return: The socket connection established. If 1 is returned, the connection failed ************/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);//convert dotted decimal IP to unsigned long reshape if (inaddr! = Inaddr_none) memcpy (&ad.sin_addr, &INADDR, S Izeof (INADDR)); else//If host is the domain name {HP = gethostbyname (host);//Use the domain name to obtain the IP 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;//Connect if (Connect (sock, (struct sockaddr *) &ad, sizeof (AD)) < 0) retur n-1; Return sock;}
WENBENCH.C Source code and comments
/* * (C) Radim Kolar 1997-2004 * This is the free software and see the GNU public License version 2 for * details. * Simple forking WWW Server Benchmark: * * Usage: * webbench--help * * Return Codes: * 0-sucess * 1-benchma RK failed (server is not on-line) * 2-bad param * 3-internal error, fork failed * */#include "socket.c" #includ E <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 Protocol version number 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_hea D 1#define method_options 2#define method_trace 3#define program_version "1.5" int method=method_get;//global variable, specifying HTTP method int clients=1;//whether the concurrent number int force=0;//waits for the server to answer. The default is not to wait for the int force_reload=0;int proxyport=80;//Proxy server port number. Default is 80char *proxyhost=null;//proxyHow long the server's address, int benchtime=30;//, runs. The default is 30s. You can specify/* internal */int Mypipe[2];char Host[maxhostnamelen] via-t, #define Request_size 2048char request[request_size];// struct option struct, with the Getopt_long function using the 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 CHA R *url); static void Alarm_handler (int signal) {Timerexpired=1;} /************** program use method ***************/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-PR agma: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 Protoco l.\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)//use method not appropriate {usage (); return 2; }//Check the input parameters and set the corresponding options 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);//Enter version number case ' t ': Benchtime=atoi (Optarg); Break;//optarg represents a parameter after the command, for example-C 100,optarg is 100. Atoi represents converting a string to a growth integer. Case ' P ':/* Proxy Server parsing Server:port */TMP=STRRCHR (Optarg, ': '); proxyhost=optarg;//set address 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", opt ARG); return 2; } *tmp=' N '; Proxyport=atoi (tmp+1); break;//Reset port number case ': ': Case ' h ': Case '? ': usage (); return 2;break; Case ' C ': Clients=atoi (OPTARG); break;//concurrent Number}}//optind is the subscript position of the corresponding parameter 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 ");//use, last URL, so optind should be the location of the URL build_request (Argv[optind]); /* Print Bench Info */printf ("\nbenchmarking:"); Switch (method) {case METHOD_GET:default:printf ("GET"), break, Case method_options:printf ("Options"); method_head:printf ("HEAD"); Case method_trace:printf ("TRACE"); } 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 "); 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 ();} /**************** Create URL request connection @url:url address create a request to place in global variable requests ****************/void build_request (const char *url) {Char TMP[10]; int i; Request address and request connection clear 0 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"); Case method_head:strcpy (Request, "HEAD"); Case method_options:strcpy (Request, "OPTIONS"); Case method_trace:strcpy (Request, "TRACE"); } strcat (Request, ""); IF (null==strstr (URL, "://"))//find "://" in the URL location {fprintf (stderr, "\n%s:is not a valid url.\n", URL); Exit (2); if (strlen (URL) >1500)//url is too long {fprintf (stderr, "url is too long.\n"); exit (2); if (proxyhost==null)//Whether the proxy server is empty if (0!=strncasecmp ("/http", url,7))//Compare the first 7 strings {fprintf (stderr, "\nonly http Protoc OL is directly supported, set--proxy for others.\n "); Exit (2); }/* Protocol/host delimiter */i=strstr (URL, "//")-url+3;//i points to http://After the first location/* printf ("%d\n", I); */if (STRCHR (url+i, '/') ==null) {fprintf (stderr, "\ninvalid url syntax-hostname don ' t end s with '/'. \ n '); Exit (2); if (proxyhost==null) {/* get port from hostname */if (index (Url+i, ': ')!=null && index (url+i, ': ') <i Ndex (Url+i, '/'))//Determine whether the port number is specified in the URL {strncpy (HOST,URL+I,STRCHR (url+i, ': ')-url-i);//Remove the host address bzero (tmp,10); strncpy (Tmp,index (url+i, ': ') +1,strchr (Url+i, '/')-index (Url+i, ': ')-1); /* printf ("tmp=%s\n", TMP); */Proxyport=atoi (TMP);//port number converted to int 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)//version number 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 ("req=%s\n", request);} /* VRACI system RC Error Kod *//*****Create pipelines and child processes to test for HTTP requests **********************/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 had 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)//Use ProxyHost 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 (stderr, "child-%d%d\n", speed,failed); */fprintf (f, "%d%d%d\n", speed,failed,bytes);//Put the results of each sub-process run into the pipeline. 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)//The parent process reads the pipe data and does addition {pid=fscanf (F, "%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); Output test results 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;} /****************************** Here's the place to test HTTP @host: Address @port: Port @req:http Format Method ********************************/void Benchcore (const char *host,const int Port,const char *req) {int rlen; char buf[1500]; int s,i; struct sigaction sa;/* Set Up Alarm Signal Handler */sa.sa_handler=alarm_handler;//timer method sa.sa_flags=0; if (Sigaction (Sigalrm,&sa,null)) exit (3); Alarm (benchtime); Rlen=strlen (req); Nexttry:while (1) {if (timerexpired)//timer is set to Timerexpired=1, the function will return {if (failed>0) {/* FPR intf (stderr, "correcting failed by signal\n"); */failed--; } return; } s=socket (Host,port); Create connection if (s<0) {failed++;continue;}//Connection failed, failed plus 1 if (Rlen!=write (S,req,rlen)) {FAILED++;CL OSE (s); continue;} Send failed if (http10==0) if (Shutdown (s,1)) {failed++;close (s); continue;} if (force==0)//force equals 0 means to read the HTTP request data { /* 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; Else bytes+=i;//reads the number of bytes increased}} if (Close (s)) {failed++;continue;} Speed++;//http test successfully, Speed plus 1}}
Webbench Source Code Analysis