闲来无事,决定整理一下最近看的一些东西,因而先写写fastcgi
协议,此协议是cgi
协议的升级版,其实就是当年cgi
太弱,致使动态页面太耗性能,因此开发了例如fastcgi
协议等升级版,下面咱们就来聊聊这个协议的相关内容。php
CGI
协议以及Fastcgi
协议的介绍CGI
协议的介绍CGI
协议的诞生是为了解决HTTP
协议与编程语言之间的链接问题,从而减低动态页面的开发难度。这个协议避免全部的编程语言开发动态页面时还须要开发一套HTTP
的解析库。
那么,关于HTTP
协议自己,其实就是2个部分:请求头部和请求体。请求头部基本上是做为键值对传输,例如Date: Sat, 03 Feb 2018 00:14:03 GMT
。请求体则是纯数据流,用于传输文件等数据。因此,CGI
自己也相应提供了2中数据格式的输入:键值对输入和数据流输入。
那最初的CGI程序的输入方式及其简单,键值对数据的输入直接利用环境变量进行传输,而数据流输入则是利用标准输入流(stdin
)进行传输。CGI
程序的返回也包含了2种:正常数据输出和错误数据输出,正常数据输出是用于输出处理后的数据信息,主要是HTTP
的响应报文,而错误数据输出则是用于在程序解析错误时返回给web
服务器的错误信息,以便于web
服务器作响应的处理和日志记录功能。正常数据输出和错误数据输出在当时的CGI
程序中也理所应当的使用了标准输出流stdout
和标准错误流stderr
。可谓是十分简约。
下面是一个简单的CGI
程序的小栗子:html
#!/bin/sh echo "Content-Type:text/html\n\n" echo "" echo "" echo "hello! This is the PATH var:" echo $PATH
这个CGI
程序主要功能就是输出了PATH
环境变量,其中会包含请求头部的相关信息(以后补充结果)。nginx
Fastcgi
协议的介绍可是CGI
程序的弊端十分显而易见:须要新的进程进行数据处理,数据传输方式没法分布式部署,使用进程致使容易影响系统运行,每次请求都从新加载数据耗费性能。因而乎,Fastcgi
程序就是为了解决相关问题而出现。Fastcgi
程序将CGI
程序的规范都进行了保留,并将其升级,主要是将输入和输出的方式从标准流迁移到了socket
传输,同时,fastcgi
协议也支持将cgi
程序进行守护进程化,这样能够提升请求的处理速度,同时提升了稳定性。c++
那么,Fastcgi
协议、php-fpm
、Nginx
三者自己是什么关系?其实就是,Nginx
是web
服务器,只提供HTTP
协议的输入和输出。php-fpm
是Fastcgi
服务器,只支持Fastcgi
协议的输入和输出。他们2者直接由Nginx
将HTTP
协议转换为Fastcgi
协议传输给php-fpm
进行处理。web
Fastcgi
协议的详解Fastcgi
协议是由一段一段的数据段组成,能够想象成一个车队,每辆车装了不一样的数据,可是车队的顺序是固定的。输入时顺序为:请求开始描述、请求键值对、请求输入数据流。输出时顺序为:错误输出数据流、正常输出数据流、请求结束描述。
其中键值对、输入流、输出流,错误流的数据和CGI
程序是同样的,只不过是换了种传输方式而已。
再回到车队的描述,每辆车的结构也是统一的,在前面都有一个引擎,引擎决定了你的车是什么样的。因此,每一个数据块都包含一个头部信息,结构以下:数据库
typedef struct { unsigned char version; // 版本号 unsigned char type; // 记录类型 unsigned char requestIdB1; // 记录id高8位 unsigned char requestIdB0; // 记录id低8位 unsigned char contentLengthB1; // 记录内容长度高8位 unsigned char contentLengthB0; // 记录内容长度低8位 unsigned char paddingLength; // 补齐位长度 unsigned char reserved; // 真·记录头部补齐位 } FCGI_Header;
注释都描述的很清楚:编程
version
为版本号,当前只有初版本。type
做为关键的描述,用于描述数据的类型,例如是键值对类型仍是数据流类型,或者是请求开始和请求结束,都是经过type
进行描述。requestId
是记录ID,(B1表明高位,B0表明低位,下文同理
),记录ID主要避免同一个socket
通道时候传输的数据的正确性,同时也提升了传输的效率。ContentLength
为数据内容的长度。paddingLength
是用于数据能进行8
字节对齐,这样对解析以及底层的IO
操做性能有提示,因此paddingLength
只是数据对8
取余,当然不会超过7
。reserved
做为保留位,主要也是为了协议头部能与8
字节对齐。关于type
的取值范围:服务器
#define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
咱们大体按照其中的顺序进行介绍。网络
FCGI_BEGIN_REQUEST
请求输入的时候,会带有该类型的数据,这样是为了描述当前须要Fastcgi
服务器充当的角色以及相关的设定。其中的数据结构为:数据结构
typedef struct { unsigned char roleB1; // 角色类型高8位 unsigned char roleB0; // 角色类型低8位 unsigned char flags; // 小红旗 unsigned char reserved[5]; // 补齐位 } FCGI_BeginRequestBody;
官方在升级CGI
的时候,同时加入了多种角色给Fastcgi
协议,其中定义为:
#define FCGI_RESPONDER 1 #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3
其中FCGI_RESPONDER
是咱们最多见的动态语言脚本处理角色,叫作响应器。FCGI_AUTHORIZER
是用于判断请求是否拥有访问权限,相似于HTTP
请求中的认证功能,叫作受权器。FCGI_FILTER
是用于对一些特殊的数据进行处理并返回,包括添加数据头部与尾部等功能,叫作过滤器(官方对其没有过多的介绍,因此没法详细描述)。
大多数请求咱们都是使用FCGI_RESPONDER
角色进行请求传输,由于动态语言能够彻底的替代其余2中角色的功能,因此受权器和过滤器的功能被你们给遗忘了。不过这不表明角色的设定是错误的,角色的设定很大一部分程度上给Fastcgi
协议提供了快捷扩展的功能,保证了协议的可扩展性。flags
则是用于设置使用传输时复用通道,避免每次传输都须要新开一个socket
通道来浪费时间和性能。
FCGI_ABORT_REQUEST
该类型主要是给web
服务器提供主动结束通道的功能,场景为当web
服务器须要尽快结束并关闭通道,则会发送该请求给Fastcgi
服务器,这样Fastcgi
服务会尽快的将数据处理完并返回关闭通道。
FCGI_END_REQUEST
该类型是当响应数据输出完毕后,用于描述该请求的响应结果,相似于HTTP的响应报文的状态码,数据结构以下:
typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody;
其中appStatus
相似于HTTP
请求的状态码,主要用于描述数据处理的状况,而protocolStatus
主要用于对于这次请求通道的描述,是请求正常完成仍是拒绝完成等,其中的赋值范围以下:
#define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3
区别以下:
可是,通常状况下,你们都只返回appStatus
为0以及protocolStatus
为0的数据。这其实也是因为官方对相关的描述并不充分的缘由。
FCGI_PARAMS
该结果主要用于传输键值对类型数据,毕竟英文翻译叫参数。其中该类型为了节约空间提供了4
类结构体:
typedef struct { unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */ unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */ unsigned char nameData[nameLength]; unsigned char valueData[valueLength]; } FCGI_NameValuePair11; typedef struct { unsigned char nameLengthB0; /* nameLengthB0 >> 7 == 0 */ unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */ unsigned char valueLengthB2; unsigned char valueLengthB1; unsigned char valueLengthB0; unsigned char nameData[nameLength]; unsigned char valueData[valueLength]; } FCGI_NameValuePair14; typedef struct { unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */ unsigned char nameLengthB2; unsigned char nameLengthB1; unsigned char nameLengthB0; unsigned char valueLengthB0; /* valueLengthB0 >> 7 == 0 */ unsigned char nameData[nameLength]; unsigned char valueData[valueLength]; } FCGI_NameValuePair41; typedef struct { unsigned char nameLengthB3; /* nameLengthB3 >> 7 == 1 */ unsigned char nameLengthB2; unsigned char nameLengthB1; unsigned char nameLengthB0; unsigned char valueLengthB3; /* valueLengthB3 >> 7 == 1 */ unsigned char valueLengthB2; unsigned char valueLengthB1; unsigned char valueLengthB0; unsigned char nameData[nameLength]; unsigned char valueData[valueLength]; } FCGI_NameValuePair44;
相对晦涩的话,咱们再来看看图片描述:
如图所示,该相似分为4个部分:nameLength
、valueLength
、nameData
和valueData
,其中nameLength
和valueLength
用于描述长度,有1
字节和4
字节的2
种方案,也就构成了上面的4
种不一样的结构体。其中只须要判断第一个字节的最高位是否为1
,若为1
则是用4
字节描述的长度,若为0
则用1
字节。
其中,1
字节是char
型的大小,4
字节是int
型大小,因此十分方便解析。
类型中的FCGI_STDIN
、FCGI_STDOUT
、FCGI_STDERR
和FCGI_DATA
都是数据流传输,不存在什么结构体,内容中只有数据信息。十分暴力,如图所示:
FCGI_GET_VALUES
该类型主要用于查询fastcgi
服务器的相关性能参数,结构体复用了FCGI_PARAMS
类型的结构体,其中name
设置为相应的值,而value
为空便可。以后由fastcgi
服务返回FCGI_GET_VALUES_RESULT
类型的数据并填充value
便可。其中name
取值类型包括:
Fastcgi
协议实例0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E. 0x0010: 03dc 535f 4000 4006 e5ba 7f00 0001 7f00 ..S_@.@......... 0x0020: 0001 ee2e 2328 3093 101a 95fd 1652 8018 ....#(0......R.. 0x0030: 0156 01d1 0000 0101 080a 0004 4344 0004 .V..........CD.. 0x0040: 4344 0101 0001 0008 0000 0001 0000 0000 CD.............. 0x0050: 0000 0104 0001 037f 0100 0f1f 5343 5249 ............SCRI 0x0060: 5054 5f46 494c 454e 414d 452f 686f 6d65 PT_FILENAME/home 0x0070: 2f6d 6f62 792f 6e67 696e 782f 6874 6d6c /moby/nginx/html 0x0080: 2f69 6e64 6578 2e70 6870 0c00 5155 4552 /index.php..QUER ... ... 0x03c0: 4745 7a68 2d43 4e2c 7a68 3b71 3d30 2e39 GEzh-CN,zh;q=0.9 0x03d0: 2c65 6e3b 713d 302e 3800 0104 0001 0000 ,en;q=0.8....... 0x03e0: 0000 0105 0001 0000 0000 ..........
首先,0800
以前是mac报文头部的数据,ee2e
是ip报文头部的数据,4344
以前是tcp报文的头部,因此,0101
之后,即是咱们的fastcgi
的数据包信息。0101 0001 0008 0000
,咱们能够一一对应:version:1,type:1,requestId:1,contentLength:8,padding:0
,以后就是FCGI_BEGIN_REQUEST
的数据包:role:1,flags:0
,说明使用的是响应器功能。
以后再解析一下请求头:version:1,type:4,requestId:1,contentLength:037f,padding:1
,因此下面的结构体为FCGI_PARAMS
,继续解析:nameLength:15,valueLength:31,nameData:SCRIPT_FILENAME,valueData:/home/moby/nginx/html/index.php
,以此类推。
POST
请求报文
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E. 0x0010: 0374 0e6d 4000 4006 2b15 7f00 0001 7f00 .t.m@.@.+....... 0x0020: 0001 d5a4 2328 3da1 e47f 4aa2 48a3 8018 ....#(=...J.H... 0x0030: 0156 0169 0000 0101 080a ffff ea15 ffff .V.i............ 0x0040: ea15 0101 0001 0008 0000 0001 0000 0000 ................ 0x0050: 0000 0104 0001 02fd 0300 0f1f 5343 5249 ............SCRI 0x0060: 5054 5f46 494c 454e 414d 452f 686f 6d65 PT_FILENAME/home 0x0070: 2f6d 6f62 792f 6e67 696e 782f 6874 6d6c /moby/nginx/html ... ... 0x0340: 5450 5f43 4f4e 4e45 4354 494f 4e6b 6565 TP_CONNECTIONkee 0x0350: 702d 616c 6976 6500 0000 0104 0001 0000 p-alive......... 0x0360: 0000 0105 0001 000c 0400 6469 6469 3d63 ..........didi=c 0x0370: 6875 7869 6e67 0000 0000 0105 0001 0000 huxing.......... 0x0380: 0000 ..
响应报文:
0x0000: 0000 0000 0000 0000 0000 0000 0800 4500 ..............E. 0x0010: 05b4 30a7 4000 4006 069b 7f00 0001 7f00 ..0.@.@......... 0x0020: 0001 2328 ee28 d52b 8b2b 96bd f7d9 8018 ..#(.(.+.+...... 0x0030: 0164 03a9 0000 0101 080a 0000 bb8d 0000 .d.............. 0x0040: bb8d 0106 0001 0564 0400 436f 6e74 656e .......d..Conten 0x0050: 742d 7479 7065 3a20 7465 7874 2f68 746d t-type:.text/htm 0x0060: 6c3b 2063 6861 7273 6574 3d55 5446 2d38 l;.charset=UTF-8 0x0070: 0d0a 0d0a 4172 7261 790a 280a 2020 2020 ....Array.(..... 0x0080: 5b55 5345 525d 203d 3e20 7777 772d 6461 [USER].=>.www-da 0x0090: 7461 0a20 2020 205b 484f 4d45 5d20 3d3e ta.....[HOME].=> ... ... 0x0580: 3734 3430 322e 3335 3135 0a20 2020 205b 74402.3515.....[ 0x0590: 5245 5155 4553 545f 5449 4d45 5d20 3d3e REQUEST_TIME].=> 0x05a0: 2031 3531 3638 3734 3430 320a 290a 0000 .1516874402.)... 0x05b0: 0000 0103 0001 0008 0000 0000 0000 0000 ................ 0x05c0: 0000 ..
等,实例能够自行利用tcpdump
工具抓取。
Fastcgi
协议自己完成了对CGI
协议的升级,同时自身拥有一个很好的可扩展性,但自己功能的限制致使了市面上很好有协议功能的全部实现实例。但对其进行了解有利于对网络数据的传输的熟悉以及加深印象。
若对php/c++
等方向感兴趣且对滴滴出行有意向的小伙伴,能够将简历投送一波739609084@qq.com
,福利多多。