#CentOS on Mac#4.WebBench测试TinyHttpd
来源:互联网 发布:大数据具体应用 编辑:程序博客网 时间:2024/06/08 04:57
早有听闻WebBench和TinyHttpd是工程师不得不了解的十大优秀C语言开源项目
出乎我意料的是,两个项目的代目都十分精简约在400行左右,而且稳定性极高,将近十年未更新
任务:
1.编译运行WebBench并了解注释WebBench源代码
2.编译运行TinyHttpd并了解注释TinyHttpd源代码
3.用编译的WebBench测试编译的TinyHttpd
那就先说WebBench咯~
WebBench
源码在此
http://home.tiscali.cz/~cz210552/webbench.html
编译运行
步骤如下
1.在网站上下载源代码 wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.gz
2.解压 tar zxvf webbench-1.5.tar.gz
//tinyhttpd同理
3.
cd webbench-1.5
4.make文件
make
(1)
出现找不到ctags或gcc
yum install ctags
5.
make install
(2)error 1
mkdir -m 644 -p /usr/local/man/man1
WebBench使用
webbench -c 1000 -t 60 http://www.baidu.com/index.html
webbench -c 并发数 -t 运行测试时间 URL
x可以直接输3w或者IP地址
通过ping获取IP地址
开始测试baidu服务器的抗压性能
注意IP地址跟域名是一样的意思
咱们先试一下100个用户30s测试
可以看出全都成功了
1000个用户30s测试
=v=结果如图
100个用户60s测试
10%左右的成功率。。
前方高能WebBench400多行的
!!!源码分析!!!!
//socket.c
/* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $ * * This module has been modified by Radim Kolar for OS/2 emx *//*********************************************************************** module: socket.c program: popclient SCCS 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>//连接类型为TCP,使用IPv4网域 //host为服务器ip,clientPort建立socket连接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); //域名获取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; if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0) return -1; //出错返回-1 return sock; //正常连接,返回socket连接符}
------------------------
//webbench.c
/* * (C) Radim Kolar 1997-2004 * This is free software, see GNU Public License version 2 for * details. * * Simple forking WWW Server benchmark: * * Usage: * webbench --help * * Return codes: * 0 - sucess * 1 - benchmark failed (server is not on-line) * 2 - bad param * 3 - internal error, fork failed * */#include "socket.c"#include <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 */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 2048char 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; //命令后的参数,atoi字符转换长整型 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; } } //optind参数下标 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");//optind为URL位置 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"); 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();}//创建URL请求连接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; } strcat(request," "); 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; //i指向有用地址 /* 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 have 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) 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); //把每个子进程放到管道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){ int rlen; char buf[1500]; int s,i; struct sigaction sa; /* setup alarm signal handler */ sa.sa_handler=alarm_handler; //定时器方法 sa.sa_flags=0; if(sigaction(SIGALRM,&sa,NULL)) exit(3); alarm(benchtime); rlen=strlen(req); nexttry:while(1) { if(timer expired)//超时 { 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(shutdown(s,1)) { failed++;close(s);continue;}//发送失败 if(force==0) //读取http请求数据 { /* 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;} speed++; //测试成功 }}
参考
http://www.cnblogs.com/xuning/p/3888699.html
接下来说一说TinyHttpd~
TinyHttpd
https://sourceforge.net/projects/tinyhttpd/files/latest/download
源码在此 tinyhttpd-0.1.0.tar.gz
编译运行
步骤如下
1.在网站上下载源代码
wget http://jaist.dl.sourceforge.net/project/tinyhttpd/tinyhttpd%20source/tinyhttpd%200.1.0/tinyhttpd-0.1.0.tar.gz
2.解压
tar zxvf tinyhttpd-0.1.0.tar.gz
3.
cd tinyhttpd-0.1.0
4.make文件
make
高能出现错误!
原因分析!
TinyHttpd本来是在solaris上实现的,在socket和pthread的实现上和一般的Linux不一样。httpd.c开始的英文注释做出了对程序修改的详细指引
故(1)修改httpd.c根据开头注释
httpd.c/* J. David's webserver *//* This is a simple webserver. * Created November 1999 by J. David Blackstone. * CSE 4344 (Network concepts), Prof. Zeigler * University of Texas at Arlington *//* This program compiles for Sparc Solaris 2.6. * To compile for Linux: * 1) Comment out the #include <pthread.h> line. * 2) Comment out the line that defines the variable newthread. * 3) Comment out the two lines that run pthread_create(). * 4) Uncomment the line that runs accept_request(). * 5) Remove -lsocket from the Makefile. */#include <stdio.h>#include <sys/socket.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <ctype.h>#include <strings.h>#include <string.h>#include <sys/stat.h>#include <pthread.h>#include <sys/wait.h>#include <stdlib.h>#define ISspace(x) isspace((int)(x))#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"void *accept_request(void *);void bad_request(int);void cat(int, FILE *);void cannot_execute(int);void error_die(const char *);void execute_cgi(int, const char *, const char *, const char *);int get_line(int, char *, int);void headers(int, const char *);void not_found(int);void serve_file(int, const char *);int startup(u_short *);void unimplemented(int);/**********************************************************************//* A request has caused a call to accept() on the server port to * return. Process the request appropriately. * Parameters: the socket connected to the client *//**********************************************************************/void *accept_request(void * tclient){ int client = *(int *)tclient; char buf[1024]; int numchars; char method[255]; char url[255]; char path[512]; size_t i, j; struct stat st; int cgi = 0; /* becomes true if server decides this is a CGI * program */ char *query_string = NULL;//得到请求第一行 numchars = get_line(client, buf, sizeof(buf)); i = 0; j = 0;//客户端请求方法存到数组 while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) { method[i] = buf[j]; i++; j++; } method[i] = '\0'; if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { unimplemented(client); return NULL; }//出错//post开启cgi if (strcasecmp(method, "POST") == 0) cgi = 1;//读取url地址 i = 0; while (ISspace(buf[j]) && (j < sizeof(buf))) j++; while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; if (strcasecmp(method, "GET") == 0) {//待处理请求为url query_string = url; while ((*query_string != '?') && (*query_string != '\0')) query_string++; if (*query_string == '?') { cgi = 1; *query_string = '\0'; query_string++; } }//格式化url到path数组 sprintf(path, "htdocs%s", url); if (path[strlen(path) - 1] == '/') strcat(path, "index.html”); //默认 if (stat(path, &st) == -1) { //路径找文件 while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); not_found(client); //找不到 } else {//目录 默认index if ((st.st_mode & S_IFMT) == S_IFDIR) strcat(path, "/index.html"); if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH) ) cgi = 1; if (!cgi) //非cgi,返回服务器文件 serve_file(client, path); else execute_cgi(client, path, method, query_string); }//断开 close(client); return NULL;}/**********************************************************************//* Inform the client that a request it has made has a problem. * Parameters: client socket *//**********************************************************************/void bad_request(int client){ char buf[1024];//错误请求 sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "\r\n"); send(client, buf, sizeof(buf), 0); sprintf(buf, "<P>Your browser sent a bad request, "); send(client, buf, sizeof(buf), 0); sprintf(buf, "such as a POST without a Content-Length.\r\n"); send(client, buf, sizeof(buf), 0);}/**********************************************************************//* Put the entire contents of a file out on a socket. This function * is named after the UNIX "cat" command, because it might have been * easier just to do something like pipe, fork, and exec("cat"). * Parameters: the client socket descriptor * FILE pointer for the file to cat *//**********************************************************************/void cat(int client, FILE *resource){ char buf[1024];//读数据到socket fgets(buf, sizeof(buf), resource); while (!feof(resource)) { send(client, buf, strlen(buf), 0); fgets(buf, sizeof(buf), resource); }}/**********************************************************************//* Inform the client that a CGI script could not be executed. * Parameter: the client socket descriptor. *//**********************************************************************/void cannot_execute(int client){ char buf[1024];//无法执行 sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<P>Error prohibited CGI execution.\r\n"); send(client, buf, strlen(buf), 0);}/**********************************************************************//* Print out an error message with perror() (for system errors; based * on value of errno, which indicates system call errors) and exit the * program indicating an error. *//**********************************************************************/void error_die(const char *sc){ perror(sc); exit(1);}/**********************************************************************//* Execute a CGI script. Will need to set environment variables as * appropriate. * Parameters: client socket descriptor * path to the CGI script *//**********************************************************************/void execute_cgi(int client, const char *path, const char *method, const char *query_string){ char buf[1024]; int cgi_output[2]; int cgi_input[2]; pid_t pid; int status; int i; char c; int numchars = 1; int content_length = -1;//所有http首部读取丢弃 buf[0] = 'A'; buf[1] = '\0'; if (strcasecmp(method, "GET") == 0) while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf)); else /* POST */ {//在post的http请求中找出cotent_length numchars = get_line(client, buf, sizeof(buf)); while ((numchars > 0) && strcmp("\n", buf)) { buf[15] = '\0'; if (strcasecmp(buf, "Content-Length:") == 0) content_length = atoi(&(buf[16])); numchars = get_line(client, buf, sizeof(buf)); }//没找到 if (content_length == -1) { bad_request(client); return; } }//http状态码200 sprintf(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0);//建立管道,错误 if (pipe(cgi_output) < 0) { cannot_execute(client); return; } if (pipe(cgi_input) < 0) { cannot_execute(client); return; } if ( (pid = fork()) < 0 ) { cannot_execute(client); return; } if (pid == 0) /* child: CGI script */ { char meth_env[255]; char query_env[255]; char length_env[255];//把stdout重定向到写入端 dup2(cgi_output[1], 1);//stdin同理 dup2(cgi_input[0], 0); close(cgi_output[0]); close(cgi_input[1]);//设置环境变量 sprintf(meth_env, "REQUEST_METHOD=%s", method); putenv(meth_env); if (strcasecmp(method, "GET") == 0) {//设置环境变量 sprintf(query_env, "QUERY_STRING=%s", query_string); putenv(query_env); } else { /* POST */ sprintf(length_env, "CONTENT_LENGTH=%d", content_length); putenv(length_env); }//运行cgi程序 execl(path, path, NULL); exit(0); } else { /* parent */ close(cgi_output[1]); close(cgi_input[0]); if (strcasecmp(method, "POST") == 0)//接收post数据 for (i = 0; i < content_length; i++) { recv(client, &c, 1, 0);//把post数据写入 write(cgi_input[1], &c, 1); }//读取管道输出到客户端 while (read(cgi_output[0], &c, 1) > 0) send(client, &c, 1, 0); close(cgi_output[0]); close(cgi_input[1]);//等待子程序 waitpid(pid, &status, 0); }}/**********************************************************************//* Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combination. Terminates the string read * with a null character. If no newline indicator is found before the * end of the buffer, the string is terminated with a null. If any of * the above three line terminators is read, the last character of the * string will be a linefeed and the string will be terminated with a * null character. * Parameters: the socket descriptor * the buffer to save the data in * the size of the buffer * Returns: the number of bytes stored (excluding null) *//**********************************************************************/int get_line(int sock, char *buf, int size){ int i = 0; char c = '\0'; int n;//标准化buf数组 while ((i < size - 1) && (c != '\n')) { n = recv(sock, &c, 1, 0); /* DEBUG printf("%02X\n", c); */ if (n > 0) {//继续接受 if (c == '\r') {//接收窗口不划动 n = recv(sock, &c, 1, MSG_PEEK); /* DEBUG printf("%02X\n", c); */ if ((n > 0) && (c == '\n')) recv(sock, &c, 1, 0);//吸收换行符 else c = '\n'; } buf[i] = c; i++; } else c = '\n'; } buf[i] = '\0'; return(i);}/**********************************************************************//* Return the informational HTTP headers about a file. *//* Parameters: the socket to print the headers on * the name of the file *//**********************************************************************/void headers(int client, const char *filename){//正常http首部 char buf[1024]; (void)filename; /* could use filename to determine file type */ strcpy(buf, "HTTP/1.0 200 OK\r\n"); send(client, buf, strlen(buf), 0);//服务器信息 strcpy(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); strcpy(buf, "\r\n"); send(client, buf, strlen(buf), 0);}/**********************************************************************//* Give a client a 404 not found status message. *//**********************************************************************/void not_found(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>The server could not fulfill\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "your request because the resource specified\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "is unavailable or nonexistent.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0);}/**********************************************************************//* Send a regular file to the client. Use headers, and report * errors to client if they occur. * Parameters: a pointer to a file structure produced from the socket * file descriptor * the name of the file to serve *//**********************************************************************/void serve_file(int client, const char *filename){ FILE *resource = NULL; int numchars = 1; char buf[1024];//读取丢弃header buf[0] = 'A'; buf[1] = '\0'; while ((numchars > 0) && strcmp("\n", buf)) /* read & discard headers */ numchars = get_line(client, buf, sizeof(buf));//打开文件 resource = fopen(filename, "r"); if (resource == NULL) not_found(client); else {//写首部,复制文件 headers(client, filename); cat(client, resource); } fclose(resource);}/**********************************************************************//* This function starts the process of listening for web connections * on a specified port. If the port is 0, then dynamically allocate a * port and modify the original port variable to reflect the actual * port. * Parameters: pointer to variable containing the port to connect on * Returns: the socket *//**********************************************************************/int startup(u_short *port){ int httpd = 0; struct sockaddr_in name;//建立socket httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); memset(&name, 0, sizeof(name)); name.sin_family = AF_INET; name.sin_port = htons(*port); name.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind”);//动态随机分配 if (*port == 0) /* if dynamically allocating a port */ { socklen_t namelen = sizeof(name); if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1) error_die("getsockname"); *port = ntohs(name.sin_port); }//开始监听 if (listen(httpd, 5) < 0) error_die("listen"); return(httpd);}/**********************************************************************//* Inform the client that the requested web method has not been * implemented. * Parameter: the client socket *//**********************************************************************/void unimplemented(int client){//method不被支持 char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0);}/**********************************************************************/int main(void){ int server_sock = -1; u_short port = 0; int client_sock = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread;//建立httpd服务 server_sock = startup(&port); printf("httpd running on port %d\n", port); while (1) {//收到客户端链接请求 client_sock = accept(server_sock, (struct sockaddr *)&client_name, &client_name_len); if (client_sock == -1) error_die("accept"); /* accept_request(client_sock); */ if (pthread_create(&newthread , NULL, accept_request, (void *)&client_sock) != 0) perror("pthread_create"); } close(server_sock); return(0);}
(2)在makefile文件中改为
gcc -W -Wall -o httpd httpd.c -lpthread
make完结果
TinyHttpd使用
1.运行应用
./httpd
得到端口号为52870
2.在浏览器firefox中输入地址
http://localhost:52870
TinyHttpd源码分析
1.函数功能如下
accept_request: 处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程。 bad_request: 返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST. cat: 读取服务器上某个文件写到 socket 套接字。 cannot_execute: 主要处理发生在执行 cgi 程序时出现的错误。 error_die: 把错误信息写到 perror 并退出。 execute_cgi: 运行 cgi 程序的处理,也是个主要函数。 get_line: 读取套接字的一行,把回车换行等情况都统一为换行符结束。 headers: 把 HTTP 响应的头部写到套接字。 not_found: 主要处理找不到请求的文件时的情况。 sever_file: 调用 cat 把服务器文件返回给浏览器。 startup: 初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等。 unimplemented: 返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持。
2.注释详细如上
[综合]WebBench测试TinyHttpd
基本如上
1.用TinyHttpd小型web服务器
2.用WebBench对地址进行测试
出现错误
全部failed且TinyHttpd自动断开
开始以为并发数的问题,减小后并未改善
网上查阅资料后
解决方法
(1)原因
因为WebBench在一次访问完之后就断掉了,但是TinyHttpd要分次把一个网页的内容发送给WebBench,所以第一次发的时候是成功的,第二次就失败了。而且TinyHttpd在send的时候没有异常判断和处理,所以程序卡死
(2)解决
httpd.c的unimplemented中将后面的14行代码发送注释掉
void unimplemented(int client){ char buf[1024]; sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); /*sprintf(buf, SERVER_STRING); send(client, buf, strlen(buf), 0); sprintf(buf, "Content-Type: text/html\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</TITLE></HEAD>\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n"); send(client, buf, strlen(buf), 0); sprintf(buf, "</BODY></HTML>\r\n"); send(client, buf, strlen(buf), 0);*/}
3.make重新编译
4.运行
./httpd
运行结果全部成功
[总结]
小小的预告!
下一篇会详细解释最后最综合的网络实验,从一开始搜bing到有时候科学上网google,再到后面看cnblog、csdn找到有用的答案或错误分享解决方案,到逐渐从github找到大神。
结论:最make sense的还是开发者的README.md,自己编的程序自己最清楚嘛,次之是github的大神或者stackflow。对英文有一定要求。至于bing、google搜出来的答案当然也会有用的但是鱼龙混杂,能找到有帮助但却浪费了一定时间。
- #CentOS on Mac#4.WebBench测试TinyHttpd
- Linux中部署服务器Tinyhttpd并用Webbench测试抗压性能
- Webbench 一款Linux下的压力测试工具 for Mac
- 在CentOS下安装WebBench进行web 性能测试
- centos下安装webbench最新版本-实现并发测试
- Centos安装webbench
- apache centos 安装webbench
- tinyHttpd
- webbench 压力测试
- Nginx 压力测试 /webbench
- 网站性能测试webbench
- webbench网站压力测试
- webbench 压力测试
- 服务器压力测试 webbench
- webbench压力测试例子
- webbench压力测试工具
- Webbench-http压力测试
- 压力测试工具-WebBench
- struts2出现Could not find action or result的错误原因
- [置顶]Servlet的生命周期+实现方式
- [置顶]Request 和 Response 原理
- [置顶]会话技术( Cookie ,Session)
- [置顶]android 四大组件之---Service
- #CentOS on Mac#4.WebBench测试TinyHttpd
- [置顶]showSetPwdDialog--自定义对话框
- [置顶]popupwindow展示
- [置顶]手机APP创建桌面快捷方式
- [置顶]Android中Listview展示及其优化好处
- 跨域请求之二:jsonp详解
- [置顶]Android 面试题汇总
- windows7直接安装运行64位 haproxy1.5
- Node.js 文件操作