天天看点

tinyhttpd源码详解

tinyhttpd是一轻量级的web 服务器,最近几天终于抽出时间来研究研究了。其源码百度即可下载,500多行,确实是学习linux编程的好材料。很多网友都写了关于tinyhttpd的博文,但是我还是觉得不够深入,严格说是写得不够深入,往往就是把500多行代码一扔,分析下主要过程,画个流程图就完事了。我怎么觉得还有很多东西可以挖一挖呢,也许还可再调整一下代码,虽然目前也不清楚可调整多少,待我细细道来。

我分析的过程就按主要路线走,也就是这样一个主干道流程:服务器创建socket并监听某一端口->浏览器输入url发出请求->服务器收到请求,创建线程处理请求,主线程继续等待->新线程读取http请求,并解析相关字段,读取文件内容或者执行cgi程序并返回给浏览器->关闭客户端套接字,新线程退出

咱们先来看看main函数

这段代码,只要是稍微了解linux的网络编程就很好懂,创建服务端socket,绑定、监听、等待客户端连接。只不过作者把这些步骤都放在了一个叫startup的函数里。那来看startup

很常见的步骤,就不多说了。

此后,服务端就accept等待连接,作者其实没有关心客户端来自哪里,那accept的第二、第三参数完全可以为null。接着就创建线程把客户端套接字作为参数传过去了,由新线程处理请求,这是服务器编程的常用手段,提高并发性。注意这里的线程函数并不完全合法,至少在linux上就不符合线程函数的原型定义,编译时编译器也只是警告而未报错。

接下来重点就在线程函数accept_request上了

首先很关键一点要理解get_line的意思。我们要知道当在浏览器中输入url后enter之后,它发给服务器是文本型的字符串,遵循http请求格式,类似下面的:

get / http/1.1

host:www.abc.com

content-type:text/html

...

get_line干的事就是读取一行,并且不管原来是以\n还是\r\n结束,均转化为以\n再加\0字符结束。其实现如下:

get_line完后,就是开始解析第一行,判断是get方法还是post方法,目前只支持这两种。如果是post,还是把cgi置1,表明要运行cgi程序;如果是get方法且附带以?开头的参数时,也认为是执行cgi程序

还是获取要访问的url,可以是很常见的/,/index.html等等。该程序默认为根目录是在htdocs下的,且默认文件是index.html。另外还判断了给定文件是否有可执权限,如果有,则认为是cgi程序。最后根据变量cgi的值来进行相应选择:读取静态文件或者执行cgi程序返回结果。

我们首先看看最简单的静态文件情况,调用函数serve_file

将文件名作为参数,首先读完客户端的头部,然后打开创建文件流。为了模拟http响应,首先向客户端发送头部,头部信息至少包含以下几点:

http/1.0 200 ok

server:

content-type:

\r\n(一个空白行,标识头部结束)

最后发送数据体部分,即文件内容,在cat方法中,fgets每读入一行,就send,直到末尾。headers和cat函数就不在这里列出了。下面,我们来看看一个具体测试例子,紧接着在gdb中调试

我在根目录下的htdocs下建立一个新文件index2.html,内容如下:

<a href="http://10.108.222.96:54205/test.sh">display date</a>

我在这里放了一个链接,href部分是关于cgi的,先不管,就只看文本部分能否显示在浏览器中。

首先编译之后直接运行./httpd,程序打印"httpd running on port 53079"

我们在浏览器中访问index2.html文件,如下图所示:

tinyhttpd源码详解

文本能正确显示了。那如何在gdb中调试观察呢?

前面已说过,tinyhttpd目前就支持两种请求形式,纯get请求或者带?的get和直接post请求。了解到源码htdocs目录下的cgi都是perl写的,不知读者你懂不懂,反正博主我不懂,所以就改一改,改成自己的需求,用shell写。正如index2.html所示:

test.sh脚本如下:

#!/bin/sh 

#echo "content-type:text/html"

echo

echo "<html><head><meta charset="utf-8"><title>mytitle</title></head><body>"

time=`date`

echo "<p>server time:$time"

echo "</body></html>"

即包括服务器响应给客户的字符数据,顺便把服务器时间传过去。注意要加test.sh添加执行权限,才会被视为执行cgi程序,且href中的端口号要改为你具体的端口号,这里只是个示例。来看当在浏览器中点击“display date”时,服务器作出的响应:

结果显示:

tinyhttpd源码详解

当然我在这里只是演示了其中的一种情况,至于情况如get请求带?查询的,post请求带数据体的,只有靠读者自己去尝试了,博主暂时抛砖引玉于此。

呃,感觉讲解至此结束了呢。貌似还有一点点细节博主还得继续研究下,总之通过这个例子确实对linux编程了解了更多了,感谢开源,哈哈!

参考链接

1 http://blog.csdn.net/jcjc918/article/details/42129311

2 http://blog.sina.com.cn/s/blog_a5191b5c0102v9yr.html

3 cgi介绍:http://www.jdon.com/idea/cgi.htm

4 http://www.scholat.com/vpost.html?pid=7337

继续阅读