Tinyhttpd源码解析
Tinyhttpd是一个超轻量级的http服务器软件,使用C语言编写,代码仅有500行左右,实际生产环境中并不实用,但通过研究这短小精悍的500行代码,有助于我们了解web服务器的本质,弄清楚在访问一个网页的过程中,服务器都做了哪些工作。
源码下载&编译
TinyHttpd的原版代码下载
下载完成,解压后会得到这样一些文件:
主要需要看的是 httpd.c 里边的源码。
在这之前,先make
把代码编译一下,看一下执行后的效果。
编译的时候会报错,主要是一些线程函数和 Makefile 里的连接库错误,下面给出我的解决方案
把 Makefile 文件中的编译选项中的 -lsocket 去掉。
436行
namelen
和491行client_name_len
的变量类型改为socklen_t
accept_request用作线程的处理函数,函数原型需做出改变。
1
2
3
4
5
6
7
8void *accept_request(void *);
void *accept_request(void * arg)
{
// 此处为了尽可能少的修改原有代码,参数名改为arg
int client = *(int*)arg;
...
return NULL;
}496行
client_sock
参数类型错误,改为&client_sock
(可选)改端口号,TinyHttpd 默认的是动态绑定端口号,为方便后面使用改为固定 8080
1
u_short port = 8080; // main函数,480行
(可选)改IP地址
1
2// startup 函数 433行
name.sin_addr.s_addr = inet_addr("127.0.0.1");
至此,程序应该能正常编译通过,下面是示例程序的运行效果,
浏览器输入http://127.0.0.1:8080/index.html
(ip和端口号要以自己的实际情况为准)
ps: 此处如果第二个页面显示不出来,主要是由于缺少或找不到执行 perl 脚本的程序导致,安装 perl 并将
htdocs/color.cgi
的第一行改为自己电脑上的 perl 所在位置即可。
Tinyhttpd 运行流程
- main函数,调用 startup 创建监听套接字开始监听,循环接收客户端连接。
- 接收到一个客户端的连接后,创建一个新的线程,进入 accept_request 函数与客户端进行交互。
- 根据请求的类型,判断后续处理的方式,如果是 POST 请求或带参数的 GET 请求,则采取 CGI 的处理方式(execute_cgi 函数),否则,采取普通网页的处理方式(serve_file函数)。
- 数据传输完毕,关闭连接,线程退出。
1 | int main(void) |
对 普通网页 的处理
- 对于普通网页,TinyHttpd 会尝试打开该文件;
- 如果打开成功,会将响应头和文件内容一并发给客户端进行响应;
- 如果打开失败,会将网页找不到的错误发给客户端,这就是 404 not fount 出现的原因。
1 | void serve_file(int client, const char *filename) |
对 CGI程序 的处理
- 对于可执行的CGI程序,TinyHttpd 会 fork 一个进程进行执行。
- 通过两个管道(cgi_output、cgi_input)进行父子进程通信;
- 子进程(CGI程序)将将标准输入输出重定向至两个管道,这样的效果是:CGI程序中的标准输入会从 cgi_input 管道读取,标准输出会输出到 cgi_output 管道,所以在编写CGI程序时,只需要通过相应编程语言的输入输出函数进行操作,而不需要额外的接口。
- 父进程会将 cgi_output 管道的内容发送给到客户端,cgi_input 管道的内容发送给子进程。
1 | void execute_cgi(int client, const char *path, |