HTTPweb服务器——HTTP整体设计框架

我们整个的项目采用B/S模式,通过浏览器发送HTTP的get方法和post方法,然后server进行响应,这样最终通过html看到我们所显示的最终的效果。
另外,为了支持并发,我们采用了多线程的结构。

1、进行创建监听套接字

和其他socket编程的模式是一样的,我们这里的第一步依然是首先创建监听套接字,创建的过程依然是,

socket–>bind—>listen。

这里在bind的时候采用sockaddr_in方式,另外为了实现端口的复用,我们采用了setsockopt的SU_REUSEADDR方式。

listen的时候,我们这里经常需要关注backlog,这个是已完成队列和未完成队列和的大小。

2、 进行accpet多线程的建立。

我们进行accept接收客户端的connect请求。这个过程的实质是对backlog队列的一个操作,在accept前,内核接受到connect请求首先把socket放入未完成队列,然后accept的时候,需要把socket放入已完成队列当中去,然后accept成功以后从已完成队列当中取出。这就是accept的整个过程。

accept成功以后,我们使用pthread_create来进行创建线程,把socket托付给线程来进行操作。在线程处理的过程中,有一个问题就是线程等待,我们为了解决这个问题,我们使用线程分离,这样使得线程可以作为孤儿进程的形式托管给1号进程,当执行完毕以后,由1号进程来进行资源的回收。

3、 线程处理

在整个线程处理函数内部,我们对HTTP的请求进行分析,通过对其中的路径参数等信息,我们调用根目录下的index.html进行处理。

3.1 、进行获取HTTP信息

线程处理中,少不了的就是对HTTP报文信息的处理,从这些中提取出来有用的信息,我们采取的读取方式是按行读取。

例如对HTTP 方法的第一行进行读取,然后这一行的三个字段又是按照空格隔开,我们利用这个特性,把HTTP请求的方法(GET或POST,目前只进行处理这两个),然后可以得到资源路径(url),最后一个字段是HTTP 的版本信息。

得到这一部分资源以后,我们现在就需要进行处理,接下来就是考虑参数。HTTP请求经常会带一些参数,通过这些参数浏览器请求资源,GET方法的资源是在url当中,POST方法的资源是在消息正文当中,这样我们也就能得到资源了。

参数的形式一般为:

?xx=400&bb=22## 采用?开始,采用&连接

所以这里需要说的是,其实我们的多线程HTTP服务器总共会有三种情况:

情况 说明
非cgi 此时不带参数,我们需要返回主页给浏览器
GET cgi 此时参数在url后,并且按照一定的规律连接,我们需要把参数提出来交给cgi程序去进行运算
POST cgi 此时参数在消息正文当中,我们需要结合消息报头,提取参数

3.2 、非cgi模式

我们这里先来看下非cgi方式,这种情况下,首先明白此时我们可以得到资源路径,这个资源路径其实就是根目录下的路径,默认我们去寻找根目录下的主页。
所以我们需要给资源加上index.html
然后我们把整个index.html的信息发送给scoket。

我们在这里采用的方式是sendfile的操作,sendfile主要是实现零拷贝发送文件,实现一个高效的数据传输,并且对其进行验证。这样socket接受到主页信息,就可以显示出来网页了,当然这个过程我们也是按照HTTPPOST响应发送过去的。

3.3 、cgi模式

cgi模式是我们操作的一种方式,这种方式下我们处理带参数的HTTP请求,我们把这些参数都取出来,然后把这些参数使得cgi程序能够获取到,获取到以后就可以进行计算或者数据处理等。

具体框架如图。
HTTPweb服务器——HTTP整体设计框架_第1张图片
在这里我们的处理方式就是对这两组管道需要进行一下重定向,对于fork以后的子进程,我们把管道重定向,利用dup2系统调用,然后达到的效果就是子进程最终可以从stdin中得到父进程给的信息,而父进程也就是服务器又可以从socket得到HTTP请求的内容。然后子进程数据计算以后把数据写到stdout中,server从管道中取回数据,发送给socket。这样socket端也就是浏览器那边就可以显示出最后的结果。

在这个里面重要的还有一个点就是HTTP的参数如何传递到cgi程序当中。下面我们分开来说一下。这里我们使用的是环境变量的方式,cgi程序在子进程当中运行,可以获取到环境变量,所以就可以得到所需要的参数,具体看下面的介绍。

3.4 、GET cgi模式

get方法的时候,这个时候cgi所需要的参数是放在url当中的,所以这个时候我们就去在HTTP GET请求行的第二个内容资源路径当中进行字符串的处理,我们找‘?’,当找到以后,我们让一个指针指向这里,叫做query_string,我们把这个作为一个环境变量传递给子进程就可以了。对于GET的cgi模式,最重要的也就是method(方法)和query_string(包含参数)

3.5 、POST cgi模式

但是当我们使用POST方法的cgi模式的时候,这个时候就会有另外的问题,我们的参数在正文当中,所以我们需要在正文当中寻找,另外需要知道正文中的字节数。这个时候POST的消息报头就起作用了,它在其中阻止了name:value形式的content_length:xx这样的内容,然后获取到这个长度以后,我们就可以知道向socket读取多少长度的内容了,然后读取完以后我们就可以获得到参数,同样是按照“?”和“&”的形式进行组织的,我们取出这个内容,然后进行数据运算操作。

3.6 、父进程后续操作

我们需要说一下父进程的后续操作,父进程进行处理的时候首先需要重定向管道,这样才好进行后续的操作,然后我们进行查看方法,如果是POST方法,我们需要把HTTP的请求正文全部获取到放入和cgi程序打交道的管道当中。这样才能让cgi获取到正文信息,其他情况下,我们都需要从cgi返回到管道的结果当中进行获取返回的信息,把这个信息发送给socket。最后,当然别忘了使用waitpid等待子进程。

4 、 cgi的编写方式

cgi的编写方式我们可以叫做cgi网关协议,我们所有的cgi程序都可以套用这一套来进行操作,我们采用的传递参数方式是环境变量,其实还可以用管道进行传递,传递进管道,cgi程序从管道当中读取出来。

然后我们进行字符串处理。
因为参数的组织形式是”?data1=100&data2=200”这种形式的,所以我们要找的关键符号就是“=”和“&”,这样我们就可以取到参数,然后把参数进行运算,得到结果输出到标准输出就好了。

5、cgi操作

我们的cgi操作形式其实是可以多种多样的,我们可以提供math计算操作,也可以提供关于对数据库mysql的操作,另外,我们也可以去操作我们使用python爬虫爬下来的数据来进行操作,后续比如也可以在html上面跑一个日期类,显示下天气等信息。

你可能感兴趣的:(计算机网络)