手写WEB服务器-05-动态页面
1.前言
所谓的动态页面是跟静态页面对应的,前面我们处理http请求时,是直接从服务器本地打开一个文件发送给浏览器了,而动态页面是指,当你请求一个web资源时,web服务器会动态生成一个页面,再发送给客户端浏览器。
本文要探讨的核心,是web服务器如何动态生成了这个页面。
2.例子
以前面我们搭建开发环境时的例子为例。
客户端发起的请求是这样的:
服务器找到的文件是这样的:
实际的php文件内容是这样的:
然而客户端收到的页面内容却是这样的:
在服务端到底发生了什么,才能把一个3行的极简文件变成了641行的复杂文件呢?
这个过程就发生在下面几行代码中:
3.处理过程
首先,从php页面到html的转换涉及到php语言的解析,web服务器自己实现是不现实的。其次,php官方其实是提供这种解析功能的,该功能以独立组件的形式提供,名字叫做phpfpm。
phpfpm以独立进程的形式运行,可接受tcp连接,在编译安装php的时候指定–enable-fpm即可(可以回头看下前面的环境准备篇,里面有具体操作方法)。
phpfpm进程的侦听端口是9000:
既然是个tcp服务器,就需要有通信协议,这个协议就是FASTCGI(这也是前面代码中,我们调用的函数都含有fcgi的原因)。
所以我们要做的就是,收到php页面请求后,将其包装成fcgi协议,通过TCP连接发送给phpfpm进程,phpfpm进程会把这个php页面解释成html文本,将其包装成fcgi协议,通过TCP连接返回给web服务器,web服务器收到这个应答后,解析出html文本,为它加上一个http协议头,发送给客户端浏览器。
下面我们一一来看。
3.1 发起连接
发起连接的过程就是普通tcp协议客户端的过程,没有什么特殊的:
填上我们配置的ip地址和端口,发送即可:
其中ip地址和端口是个配置:
3.2 转发请求
转发请求的过程分4步:
- 发送开始请求协议的头和体
- 发送FCGI参数
- 发送空参数(表示参数发完了)
- 发送空标准输入(表示标准输入发完了)
其中最核心的过程是第二个,即发送FCGI参数。
FCGI参数包含这几个:
我们实际请求个页面,然后看看这几个参数需要怎么填:
我们以这个地址为例:
浏览器发来的请求:
web服务器转发给phpfpm的参数:
注意到,其中主要包含四个信息:
- 方法:GET
- 文件路径:/root/www/index.php
- 参数:p=1149
phpfpm收到这些信息后,就会打开并解释/root/www/index.php文件,并把参数p=1149传递给他。
phpfpm后台执行php代码后,就会生成html并返回给web服务器。
3.3 转发应答
web服务器向phpfpm发送完请求后,就会调用web_fcgi_recv函数接收并处理来自phpfpm的应答。
fcgi协议整体上也是”头+内容+填充字节”…”头+内容+填充字节”这种形式。整体的处理过程是,先收一个头(8字节),然后查看头中包含的内容长度信息和填充字节长度信息,然后再把这些内容和填充字节收进来。
我们要转发给客户端的html数据就包含在上面说的内容部分。
fcgi的应答是分多次发送的,也就是说,我们会收到多个”头+内容+填充字节”这种数据,所以接收的过程得写在循环中。
另外,我们收到的头中,有一个报文类型的信息,可能包括3种:
其中STDOUT说明我们发送fcgi请求没有问题,返回的是正常的html内容;STDERR则表明我们发送的fcgi请求有问题,比如请求的php文件不存在等,返回的是具体的错误信息(文本形式);END则说明phpfpm已经给我们发送了所有的应答信息,可以转发给浏览器了。
具体的转发过程也比较简单:
我们首先像发静态页面那样,先发一个http200应答,然后再从fcgi应答中提取html部分,转发给浏览器即可。