分分钟手写http server

许久之前,在我还不会写socket的时候(其实现在也还不会),看到了ChinaUnix上一个写文件浏览器的帖子,400来行的样子,下过来跑了一下,能在浏览器里访问目录,下载文件,真是神奇极了。后来,我开始看UNP了,学了点儿socket编程的时候,立马就心动要自己动手写一个了。文件浏览么,和socket相关的其实也没有多少,更多的是系统调用(例如stat获取文件信息)和HTTP协议。写完之后,略有些失落。然后,看到了mini_httpd,短短3K行出头的代码,把HTTP服务器的基本特性都实现了,而且速度还不慢。详情看链接。

然后,粗略看了一眼代码逻辑之后,我也开始了。

期间,翻了一遍《http权威指南》,终于知道之前爬豆瓣读书被封的原因(没注意robots.txt文件中对爬虫速率有限制)。

写一个简单的http服务器,首先要理清业务逻辑。在请求到来之前,服务器需要设置好相关的环境变量,例如端口号,根目录,日志文件等。然后就是等待请求的到来了。

为了增加篇幅,先列一下HTTP请求格式:

通用格式为:

  请求方式    请求路径(如果是get且带参数则参数会跟在路径后面,以一个?分隔)    协议版本号(例如HTTP/1.1)

  其他请求头(以名称:值)的形式分行出现,一行一个。

请求首部以一个空行结束。在程序中反应出来就是\r\n两遍结束首部内容。

请求之后是请求包的数据部分,对如今的浏览器来说,似乎都需要有一个content-length首部标记,不然会卡在那里等待。

收到请求之后,服务器,首先分析请求头(即第一行),获取请求方式,如果方式未实现则返回400错误。然后是分析请求url,通常这里是相对URL,在首部里还有一个host标记,但有一段时间我收到了好多次绝对URL,导致URL处理错误,百思不得其解,郁闷。

对于POST请求来说,关键在于,如果带参数,则参数跟在首部后面的请求数据中(GET请求通常没有数据部分)。

根据这些信息,基本就可以开始写了。期间令我纠结的还有一个问题,对于一般的HTTP服务器来说,不可能仅仅用于显示单纯的静态页面,还需要根据参数显示不同的页面,而静态的HTML页面并不能够处理参数。所以,练手的话可以选择使用cgi方式,即把参数传递到cgi程序去,然后让cgi程序生成页面显示。具体的实现使用exec函数,需要注意的更多的是html中form的跳转似乎要加相对路径及文件全名,也就是说,form里action写了什么就会跳转到什么地方去。单纯写个文件名就会跳转到文件名那个文件去(实际上可能带后缀名)。

处理请求时,通常建议fork一个新进程去处理,方便快捷。关于响应中首部的填写,需要注意的是content-length,content-type,Connection几项,其他的,随意了。

然后,实现过程中主要用到的一些函数。按照业务逻辑来:

程序启动后,处理参数,除了自己写外,还可以用系统提供的getopt和getopt_long,后者比前者多了类似--host的支持,不过稍微测试了下,发现似乎有bug,例如-a合法,--b不合法,但--ba合法(大致是这样)。需要注意的是,短参数是根据参数选项后面有没有冒号来判断是否有参数值的(-h localhost这种),也就是说如果每个选项都要有参数值,则参数字符串应该以一个冒号结尾,例如我的:"H:P:R:"。

socket相关函数,我用简单、粗暴的read和write以及close等

收到请求后,可以通过strcasecmp来比较,忽略大小写,其余和strcmp一样。然后就是根据access判断请求的文件是否存在。如果文件存在,用stat函数判断是否具有访问权限以及获取文件大小(在响应中用于content-length首部)。

大致逻辑就是这样了。熟悉了一下HTTP协议,又练了下socket编程。

posted @ 2013-04-25 21:12  fityme  阅读(866)  评论(0编辑  收藏  举报