Re-layout The source code, easy to read:
/* * (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>/* timeout token, when set to 1 o'clock, all child processes exit */volatile int timerexpired=0;/ * Number of Successful requests */int speed = 0;/* Failed requests */int failed = 0;/* Read Bytes total */int bytes = 0;/* control of HTTP protocol version */int HTTP10 = 1; /* 0-http/0.9, 1-http/1.0, 2-http/1.1 *//* supported HTTP methods */#define Method_get 0#define method_head 1#define method_opt IONS 2#define method_trace 3#define program_version "1.5" int METHOD = Method_get; /* Default to get method */int clients = 1; /* Start a client (child process) by default */int force = 0; /* Whether to wait for response data to return, 0-wait, 1-no wait */int force_reload = 0; /* Whether to send Pragma:no-cache */int proxyport = 80; /* Proxy port */char *proxyhost = NULL; /* Proxy server name */int benchtime = 30; /* Execution time, when the child process execution time is over this number of seconds, send SIGALRM signal, set timerexpired to 1, let all child processes exit * * Pipeline, the child process to complete a request, the write end writes the data, the main process reads the data from the reading end */int Mypipe[2];char Host[maxhostnamelen]; /* Host name (64 bytes) */#define REQUEST_SIZE 2048char request[request_size]; /* Request string (HTTP header) *//* command-line option configuration table, man getopt_long */static const struct option long_options[] = {' force ', no_argument, &force, 1}, {"Reload", No_argument, &force_reload, 1}, {"Time", Required_argument, N ULL, ' 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_a Rgument, &method, method_trace}, {"Version", No_argument, NULL, ' V '}, {"Proxy", req Uired_argument, NULL, ' P '}, {"Clients", required_argument, NULL, ' C '}, {null, 0, NULL, the 0}};/* child process performs the requested task of the function */static void Benchcore (const char* HOST,CONST int port, const char *request);/* Execute The main entry function of the */static int bench (void);/* generates an HTTP header */static void build_request (const char *url);/* Sets timerexpired to 1 so that all child processes Exit */static void Alarm_handler (int signal) {timerexpired=1;} /* Print help */static void usage (void) {fprintf (stderr, "webbench [option] ... Url\n ""-f|--force Don ' t wait for reply from server.\n ""-r|--reload Send rel Oad 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 U SE 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; }/* parameter explanation, see Man Getopt_long * * while (opt = Getopt_long (argc, argv, "912vfrt:p:c:?h ", Long_options, &options_index))! = EOF) {switch (opt) {case 0:bre Ak 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 P ORT number is missing.\n ", optarg); return 2; } *tmp= ' + '; 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; } 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 "); BUild_request (Argv[optind]); /* The last non-option parameter is treated as a URL */* 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"); } 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 ();} /* Generate HTTP Header */void build_request (const char *url) {char tmp[10]; int i; Bzero (host, Maxhostnamelen); Bzero (Request, request_size); /* Protocol Adaptation * */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 } strcat (Request, ""); if (NULL = = Strstr (URL, "://")) {fprintf (stderr, "\n%s:is not a valid url.\n", URL); Exit (2); } if (strlen (URL) >) {fprintf (stderr, "url is too long.\n"); Exit (2); } if (proxyhost = = NULL) {if (0! = strncasecmp ("http//", URL, 7)) {fprintf (stderr, "\nonly http Pro Tocol 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); } 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 ("req=%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); /* 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 */* This break is very important, it mainly allows the child process can only be generated from the parent process, otherwise the child process will generate the child process */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 */* sub-Process Execution request */if (proxyhost = = NULL) {Ben Chcore (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); */* Writes results to the pipeline */fprintf (f, "%d%d%d\n", speed, failed, bytes); Fclose (f); return 0; } else {/* parent process reads the pipe, prints the result */printf ("Parent%d\n", Getpid ()); f = Fdopen (Mypipe[0], "R"); if (f = = NULL) {perror ("open pipe forReading 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)/(b enchtime/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; /* This is the key, when the program executes to a specified number of seconds, send SIGALRM signal */sa.sa_handler = Alarm_handler; sa.sa_flags = 0; if (Sigaction (SIGALRM, &sa, NULL)) {exit (3); } alarm (Benchtime); Rlen = strlen (req); /* Unlimited execution of the request until timerexpired returns to 1 o'clock */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 it is http/0.9 then close the write operation of the socket */if (shutdown (s, 1)) {F ailed++; Close (s); Continue }/* If you wait for the response data to return, read the response data, calculate the number of bytes transferred */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; } else {bytes + = i; }}}} if (Close (s)) {failed++; Continue }/* Successfully completed a request and counted */speed++; }}
Source Reading notes: webbench-1.5