tinyhttpd ------ C 语言实现最简单的 HTTP 服务器

工作流程:

1>服务器启动,在指定端口或随机选取端口绑定httpd服务。

2>收到一个http请求时(其实就是listen端口accept的时候),派生一个线程运行accept_request函数。

3>取出http请求中method(getpost)url,对于get方法,如果有携带参数,则query_string指针指向url?后面的get参数。

4>格式化urlpath数组,表示浏览器请求的文件路径,在tinyhttpd中服务器文件是在htdocs文件夹下。当url/结尾,或者url是个目录,则默认在path中加上index.thml,表示访问主页。

5>如果文件路径合法,对于无参数的get请求,直接输出服务器文件到浏览器,即用http格式写到套接字上,跳到(10)。其他情况(带参数getpost方法,url为科执行文件),则调用execute_cgi函数执行cgi脚本。

6>读取整个http请求并丢弃,如果是post则找出content-length,把http状态码200写到套接字里面。

7>建立两个管道,cgi_inputcgi_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] = '