工作流程:
1>服务器启动,在指定端口或随机选取端口绑定httpd服务。
2>收到一个http请求时(其实就是listen端口accept的时候),派生一个线程运行accept_request函数。
3>取出http请求中method(get或post)和url,对于get方法,如果有携带参数,则query_string指针指向url中?后面的get参数。
4>格式化url到path数组,表示浏览器请求的文件路径,在tinyhttpd中服务器文件是在htdocs文件夹下。当url以/结尾,或者url是个目录,则默认在path中加上index.thml,表示访问主页。
5>如果文件路径合法,对于无参数的get请求,直接输出服务器文件到浏览器,即用http格式写到套接字上,跳到(10)。其他情况(带参数get,post方法,url为科执行文件),则调用execute_cgi函数执行cgi脚本。
6>读取整个http请求并丢弃,如果是post则找出content-length,把http状态码200写到套接字里面。
7>建立两个管道,cgi_input和cgi_output,并fork一个子进程。
8>在子进程中,把stdout重定向到cgi_output的写入端,把stdin重定向到cgi_input的读取端,关闭cgi_input的写入端和cgi_output的读取端,是指request_method的环境变量,get的话设置query_string的环境变量,post的话设置content-length的环境变量,这些环境变量都是为了给cgi脚本调用,接着用execl运行cgi程序。
9>在父进程中,关闭cgi_input的读取端和cgi_output的写入端,如果post的话,把post数据写入到cgo_input,已被重定向到stdin读取cgi_output的管道输出到客户端,等待子进程结束。
10>关闭与浏览器的链接,完成一次http请求与回应,因为http是无连接的。
1 /* J. David's webserver */ 2 /* This is a simple webserver. 3 * Created November 1999 by J. David Blackstone. 4 * CSE 4344 (Network concepts), Prof. Zeigler 5 * University of Texas at Arlington 6 */ 7 /* This program compiles for Sparc Solaris 2.6. 8 * To compile for Linux: 9 * 1) Comment out the #include <pthread.h> line. 10 * 2) Comment out the line that defines the variable newthread. 11 * 3) Comment out the two lines that run pthread_create(). 12 * 4) Uncomment the line that runs accept_request(). 13 * 5) Remove -lsocket from the Makefile. 14 */ 15 #include <stdio.h> 16 #include <sys/socket.h> 17 #include <sys/types.h> 18 #include <netinet/in.h> 19 #include <arpa/inet.h> 20 #include <unistd.h> 21 #include <ctype.h> 22 #include <strings.h> 23 #include <string.h> 24 #include <sys/stat.h> 25 #include <pthread.h> 26 #include <sys/wait.h> 27 #include <stdlib.h> 28 29 #define ISspace(x) isspace((int)(x)) 30 31 #define SERVER_STRING "Server: jdbhttpd/0.1.0 " 32 33 void accept_request(int); 34 //处理从套接字上监听到的一个HTTP请求,在这里可以很大一部分的体现服务器处理请求的流程 35 void bad_request(int); 36 //返回给客户端这是个错误请求,HTTP状态码是400 BAD REQUEST 37 void cat(int, FILE *); 38 //读取服务器上某个文件写到socket套接字 39 void cannot_execute(int); 40 //主要执行在处理cgi程序的处理,也是个主要函数 41 void error_die(const char *); 42 //把错误信息写到perror并退出 43 void execute_cgi(int, const char *, const char *, const char *); 44 //运行cgi程序的处理,也是哥主函数 45 int get_line(int, char *, int); 46 //读取套接字的一行,把回车换行等情况都统一为换行符结束 47 void headers(int, const char *); 48 //把HTTP相应头写到套接字 49 void not_found(int); 50 //主要处理找不到请求的文件时的情况 51 void serve_file(int, const char *); 52 //调用cat把服务器文件返回给浏览器 53 int startup(u_short *); 54 //初始化httpd服务,包括建立套接字,绑定端口,进行监听等 55 void unimplemented(int); 56 //返回给浏览器表示接收到的http请求所用的method不被支持 57 58 /**********************************************************************/ 59 /* A request has caused a call to accept() on the server port to 60 * return. Process the request appropriately. 61 * Parameters: the socket connected to the client */ 62 /**********************************************************************/ 63 void accept_request(int client) 64 { 65 66 char buf[1024]; 67 int numchars; 68 char method[255]; 69 char url[255]; 70 char path[512]; 71 size_t i, j; 72 struct stat st; 73 int cgi = 0; /* becomes true if server decides this is a CGI 74 * program */ 75 char *query_string = NULL; 76 77 numchars = get_line(client, buf, sizeof(buf)); 78 //读取 client端发送的数据并且 返回的参数是 numchars 79 i = 0; 80 j = 0; 81 while (!ISspace(buf[j]) && (i < sizeof(method) - 1)) 82 { 83 method[i] = buf[j]; 84 i++; 85 j++; 86 } 87 //得到传递的参数是post 还是get方法 还是其他 88 method[i] = '