共计 2728 个字符,预计需要花费 7 分钟才能阅读完成。
咱们先了解一下这个项目最终能到达的一个方针,然后以这个来进行项目的剖析:
1、实现最基本的 HTTP/1.0 版本的 web 服务器,客户端能够使用 GET、POST 方法请求资源
2、服务器将客户请求的资源以 html 页面的形似呈现,并能够进行差错处理(如:客户请求的资源不存在时,服务器能够返回一个 404 的页面)
3、服务器能进行简单的 cgi 运行。比如当客户在表单中输入数据后,服务器能够将运行结果返回个客户
4、能够通过页面对数据库进行操作,如增删查改等操作
一、http 服务器实现的基本框架
- 关于 HTTP 协议
即超文本传输协议,是互联网上应用最广泛的网络协议。它是应用层的协议,底层是基于 TCP 通信的。HTTP 协议的工作过程:客户通过浏览器向服务器发送文档请求,浏览器将请求的资源回应给浏览器,然后关闭连接。即:连接 -> 请求 -> 响应 -> 关闭连接。 - 关于 URL
即统一资源定位符,每个网页都对应一个 URL 地址(俗称网址),具有全球唯一性。它包含的信息指出文件的位置以及浏览器应该怎么处理它。一个完整的 URL 包括协议类型、主机类型、路径和文件名。
http 协议的 URL 格式:http: //host[:port][abs_path],http 表示使用 http 协议来进行资源定位;host 是主机域名;port 是端口号,一般有默认的;abs_path 代表资源的路径。
这里我主要介绍项目中涉及的 URL 的两种格式—URL 带参数和不带参数的。
GET 方法使用的是带参数的 URL,即传递的参数会使用?连接在资源路径后边;POST 方法使用的是不带参数的 URL,它的参数是通过 http 请求报头中的请求消息体传递给服务器的。 - 关于 HTTP 的请求与响应格式
响应报头中的状态码和状态码描述,例如:当请求的资源不存在时,会收到“404 NotFound”的页面,404 就是状态码,“NotFound”就是状态码描述,即请求的文件不存在。
二、服务器实现的基本思路
1、http 协议是基于 TCP 通信的协议,因此,实现 web 服务器的第一步至少要能实现两个主机不同进程之间的 TCP 通信。
2、接下来的部分就是比较主要的处理逻辑了,当服务器收到请求后,首先应该分析请求方法(因为 web 服务器是要支持 cgi 的,但请求方法不同处理 cgi 也不同,这里我们只处理 GET 和 POST 方法)。
3、当方法确定后,应该拿到请求的 URL,这一步是为了我们后边能处理 GET 和 POST 方法的 cgi(GET 和 POST 的参数位置不同,GET 的参数在 URL 中,POST 的参数在请求正文中)
4、判断资源是否存在,如果存在,判断这个资源是一个目录、普通文件还是一个可执行程序。之前几步我们已经提取到 URL 以及参数。GET 方法:如果没有参数,就直接将请求的资源返回(即进入非 cgi 模式运行);否则,进入 cgi 模式内部运行;只要是 POST 方法就需要支持 cgi:直接进入 cgi 函数内部运行。
非 cgi 模式:
进入非 cgi 模式时一定是 GET 方法且没有参数,此时进入 echo_www()函数内部即可,该函数会将所请求的资源以 html 的格式返回给浏览器。
cgi 模式:
上述这张图描述了运行 cgi 时的过程,首先服务器要从浏览器上读取参数,然后需要 fork 出一个子进程进行 cgi 部分的处理,父进程通过环境变量的方式将参数转交给子进程,子进程运行完成后,将结果交给父进程,父进程再将数据输出给浏览器。在这个过程中可以将父进程看作一个所谓的中间量,只进行了参数的转交,因此可以将子进程的输入输出文件描述符进行重定向,即子进程直接与浏览器“联系”。
下面总结出父子进程内部各自需要干的事情:
三、错误处理
错误处理这部分的实现可以参考 echo_www()函数,但需要改变响应的消息报头的格式,即改变状态码,状态码描述,以及返回的页面。例如当请求的资源不存在时,服务器需要返回给浏览器一个默认的 404 页面,告诉客户请求的资源不存在。效果如图:
四、项目文件
目录:
cgi:运行 cgi 部分的实现代码
conf:配置文件,存放需要绑定的服务器的 ip 和 port
log:shell 的日志文件以及 http 错误处理的日志文件
lib:MySQL 需要的 lib 库
sql_client:mysql 部分的 API 及 CGI 实现
wwwroot:web 服务器工作的根目录,包含各种资源页面(例如默认的 index.html 页面,差错处理的 404 页面),以及执行 cgi 的可执行程序
文件:
configure.sh:sheel 脚本,运行该 shell 脚本后需要自动生成 Makefile 文件
http_ctl.sh:服务器控制脚本,需要实现服务器的启动、暂停以及重新启动
httpd.pid:与 http_ctl.sh 配合使用。如果把服务器变成守护进程在后台运行,重新启动时就需要检测服务器是否启动,该文件存放服务器启动以后的进程 id
httpd.h:服务器的方法声明
httpd.c:方法实现
main.c:服务器的主逻辑
五、实现结果
请求资源存在:
运行 cgi 后:
六、源码:
https://github.com/lybb/Linux/tree/master/httpd
附:
这里是我遇到的一些问题,粘出来,也可能是你遇到的问题:
1、本地环回测试 ok,Linux 下的浏览器测试也可以,但不能接外部的浏览器访问(没有设置桥接模式)嗯~ 要是在外部浏览器测试的话千万别忘记关闭防火墙
2、服务器应答时,没有将 html 格式的页面发送,而是将底层的实现代码展示在浏览器,并且在调试时将本来要打印的调试信息会打印到网页上(在回应空行时将 send 期望发送的数值写的太大,本来只需要发送两个字节的内容)
解决:先检查代码,思路正确,在容易出现问题的地方加入调试信息,最后将问题定位在 echo_www()函数内
3、不能显示图片(这个问题是没有将所有发送的情况考虑完全,只考虑到目录、可执行程序,但没有考虑到如果请求的是一个路径明确的普通文件)
解决:测试请求一个路径明确的 test.html 文件,加入调试信息,将问题定位在:如果请求的资源存在,应该如何处理。对于普通文件,找到后并回显给浏览器;如果是目录,应答的是默认页面;如果是可执行程序,执行后返回结果
4、能显示图片后,但显示的不完整(原因:echo_www 中,期望读取一行信息的 line 值太小,不能存下一张图片)
5、运转 cgi 模式时,每次提交数据并进行 submit 后都会主动出现提示下载的页面
因素:在响应报头中,将 Content-Type 中的”text”写成”test”。而浏览器关于不能辨认或解析的实体,都会提示用户下载。