我们先了解一下这个项目最终能达到的一个目标,然后以这个来进行项目的分析:
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页面,告诉客户请求的资源不存在。效果如图:
四、项目文件
目录:
output:build.sh运行后,生成次文件夹,这个是对外发布版本
build.sh: shell 脚本,一建编译整个工程
lib:mysql需要的lib库
sql_client:mysql部分的API及CGI实现
wwwroot:web服务器工作的根目录,包含各种资源页面(例如默认的index.html页面,差错处理的404页面),以及执行cgi的可执行程序
Makefile:编译整个工程,并加进了子目录中的小Makefile合并在了一起
httpd.c:方法实现
main.c:服务器的主逻辑
五、实现结果
请求资源存在:
运行插入后:
点击查询书友信息:
可见cgi运行成功!下面我们一起看一下在线阅读功能
在首页点击:“更多书籍点击在线阅读”
这里是我收集的计算机电子版的图书,下来我们随便点开一本看看
下面让我们看看下载图书的功能:
六、码云源码:
https://gitee.com/yangxudong0058/book_club
注意:这里的lib库,你看你mysql版本是多少,看准再下,在你们虚拟机上下,lib库没传
附:
这里是我遇到的一些问题,粘出来,也可能是你遇到的问题:
1、本地环回测试ok,Linux下的浏览器测试也可以,但不能接外部的浏览器访问(没有设置桥接模式)嗯~要是在外部浏览器测试的话千万别忘记关闭防火墙
2、这个问题比较扯,在我显示html网页的时候,发现怎么都显示不出来,查了几遍代码,都对着,我很头疼,然后开始了漫长的调试,我没用gcc,我是直接打印法判断程序走到哪崩溃了,我还是发现都好好的,最后检查的时候发现html文件名,我多打了一个空格。。。。这个算是我遇到的第一个坎
3、不能显示图片(这个问题是没有将所有发送的情况考虑完全,只考虑到目录、可执行程序,但没有考虑到如果请求的是一个路径明确的普通文件)
解决:测试请求一个路径明确的test.html文件,加入调试信息 ,将问题定位在:如果请求的资源存在,应该如何处理。对于普通文件,找到后并回显给浏览器;如果是目录,应答的是默认页面;如果是可执行程序,执行后返回结果
4、能显示图片后,文字部分显示为乱码,这个问题是charset格式问题,后来改成了utf-8,问题解决
5、在我加入电子书以后,我发现请求总是不成功,但是在本地可以打开电子书,这里我找了好久找到个合适的插件,就在我用插件后,我发现还是不成功,心灰意冷。。。。突然我想到以前返回的是html网页,现在我要返回的pdf格式啊,我就去把content-type改成返回pdf格式的了,在资源路径path,后面加判断,遇到pdf结尾的,就更改content-type,如此一来,大功告成。
有一个小插曲,我在写好Makefile后,然后运行build.sh,然后去output中去发布,我发现总是提醒我找不到这本书,我当时蒙了,说我一个文件路径不存在,可是我存在呀,然后我就退出去看我源文件的路径,我当时很奇怪,不知道什么原因,后来我找到原因了,比较。。。扯,因为这个小说是我后来加的,我在总Makefile中,就没有写上把这本小说也拷到output里,这里说的找不见,是在output发布版本里找不见,我却跑到源路径下去找它去了,由于当时目录名字什么的都一样(以为我拷了一份在output),我当时误以为是这个文件里的,其实报错报的是output那个发布工程的,唉!然后就修改了Makefile,然后就好了
再一个,提醒一下各位,想在你虚拟机实现的朋友,lib库,自己在mysql官网上下载啊,不要从windows下载好往进拉,这样子容易出问题,看好你MySQL的版本,下准确,注意还有的同学下了windows的 ,我也是醉了