fastcgi

nginx和php-fpm是通过fastcgi协议进行通信的,php-fpm本身就是个socket服务端,nginx拿到url后,识别出.php结尾的地址后,会传给php-fpm,php-fpm通过预先启动的进程,调用php解释器

fastcgi消息有10种,其中有

fcgi_begin_request

fcgi_end_request

fcgi_params //环境变量,QUERY_STRING get后面的数据

fcgi_stdin //POST数据

fcgi_stdout //php-fpm返回给nginx的数据

fcgi_params key value格式 name长度value长度namevalue

长度小于128,使用一个字节

长度大于等于128,使用四个字节

首字节的第1位

0表示小于128  所以一个字节能表示最大的数字为2的7次方-1=127

1表示大于等于128, 四个字节能表示的最大的数字为2的31次方-1

 例如name为QUERY_STRING

  value为name=taek&age=20

  那么实际内容为1216QUERY_STRINGname=taek&age=20

QUERY_STRING的长度为12,12小于128,所以一个字节就能装下了

12的类型为int,其2进制为

00000000 00000000 00000000 00001100

将12强制转为char类型, 就是00001100

 

 

int nameLen=32960;

char *name="abcdefg....";

int valueLen=3;

char *value="abc";

name的长度为32960, 那么就需要4个字节来装入,value的长度为3,一个字节就能装入

分配内存 char *p=(char*)malloc(4+1+128+3);

一.保存nameLen

32960的二进制为

第一个字节  第二个字节   第三个字节  第四个字节

00000000  00000000  10000000  11000000

 

1)先右移24位再与0x80做或运算,将最后一个字节的首位置1,并强转为char类型

00000000 00000000 00000000 00000000

即*p++ = (char) ( (nameLen>> 24) | 0x80) 保存的是10000000

 

2)再右移16位,强转为char

00000000 00000000 00000000 00000000

即*p++= (char )( nameLen>> 16) 保存00000000

3)再右移8位,强转为char类型

00000000 00000000 00000000 10000000

即 *p++=(char)(nameLen >>8)

 

4) 直接转为char

11000000

即 *p++=(char) nameLen;

p[0] 到p[3]为

 10000000 00000000 10000000  11000000

二.保存valueLen

3的二进制为 00000000 00000000 00000000 00000011

直接强转为char 

*p++ = (char) valueLen;

三.保存 name

while(*name!='\0'){

  *p++=*name++;

}

四。保存value

while(*value!='\0'){

  *p++=*value++;

}

 

取数据时, 发现p[0]为10000000,大于等于128, 说明占用四个字节,

int result =0;

result = p[0] 即 00000000 00000000 00000000 10000000  

第一个字节为 result = p[0] << 24 & 0x7f 

左移24位后为

100000000 00000000 00000000 00000000 

再与0x7f做与运算 结果为

00000000 00000000 00000000 00000000

第二个字节为 *result |= p[1] << 16 

左移16位后为

00000000 00000000 00000000 00000000

第三个字节为 00000000 00000000 00000000 10000000 左移8位结果为

00000000 00000000 100000000 00000000

*result |= p[2] << 8

 

第四个字节为*result |= p[3]  

00000000 00000000 00000000 11000000

 

这四个值做或运算 即为

00000000 0000000 10000000 11000000

 

 

fastcgi 的头类型有以下几种

 

 

 

 

 

  

 

 

 消息类型

typedef enum _fcgi_request_type {
    FCGI_BEGIN_REQUEST        =  1, /* [in]                              */
    FCGI_ABORT_REQUEST        =  2, /* [in]  (not supported)             */
    FCGI_END_REQUEST        =  3, /* [out]                             */
    FCGI_PARAMS                =  4, /* [in]  environment variables       */
    FCGI_STDIN                =  5, /* [in]  post data                   */
    FCGI_STDOUT                =  6, /* [out] response                    */
    FCGI_STDERR                =  7, /* [out] errors                      */
    FCGI_DATA                =  8, /* [in]  filter data (not supported) */
    FCGI_GET_VALUES            =  9, /* [in]                              */
    FCGI_GET_VALUES_RESULT    = 10  /* [out]                             */
} fcgi_request_type;

 

 

 消息头

typedef struct _fcgi_header {
    unsigned char version;
    unsigned char type;
    unsigned char requestIdB1;
    unsigned char requestIdB0;
    unsigned char contentLengthB1;
    unsigned char contentLengthB0;
    unsigned char paddingLength;
    unsigned char reserved;
} fcgi_header;

version标识FastCGI协议版本。

type 标识FastCGI记录类型,也就是记录执行的一般职能。

requestId标识记录所属的FastCGI请求。用两个字节表示,高字节放低地址,低字节放高地址,

contentLength记录的contentData组件的字节数。可理解为消息体数据的长度,用两个字节表示,高字节放低地址,低字节放高地址,故每次发送的消息体长度最大不能超过2的16次方-1 = 65535

 

FCGI_BEGIN_REQUEST 的定义

typedef struct _fcgi_begin_request {
    unsigned char roleB1;
    unsigned char roleB0;
    unsigned char flags;
    unsigned char reserved[5];
} fcgi_begin_request;

 

 

role表示Web服务器期望应用扮演的角色。分为三个角色(而我们这里讨论的情况一般都是响应器角色)

 

typedef enum _fcgi_role {
    FCGI_RESPONDER  = 1,
    FCGI_AUTHORIZER = 2,
    FCGI_FILTER     = 3
} fcgi_role;

 

 

FCGI_END_REQUEST 的定义

typedef struct _fcgi_end_request {
    unsigned char appStatusB3;
    unsigned char appStatusB2;
    unsigned char appStatusB1;
    unsigned char appStatusB0;
    unsigned char protocolStatus;
    unsigned char reserved[3];
} fcgi_end_request;

字段解释

appStatus组件是应用级别的状态码。

protocolStatus组件是协议级别的状态码;protocolStatus的值可能是:

FCGI_REQUEST_COMPLETE:请求的正常结束。

FCGI_CANT_MPX_CONN:拒绝新请求。这发生在Web服务器通过一条线路向应用发送并发的请求时,后者被设计为每条线路每次处理一个请求。

FCGI_OVERLOADED:拒绝新请求。这发生在应用用完某些资源时,例如数据库连接。

FCGI_UNKNOWN_ROLE:拒绝新请求。这发生在Web服务器指定了一个应用不能识别的角色时。

protocolStatus在 PHP 中的定义如下

typedef enum _fcgi_protocol_status {
    FCGI_REQUEST_COMPLETE   = 0,
    FCGI_CANT_MPX_CONN      = 1,
    FCGI_OVERLOADED         = 2,
    FCGI_UNKNOWN_ROLE       = 3
} dcgi_protocol_status;

需要注意dcgi_protocol_statusfcgi_role各个元素的值都是 FastCGI 协议里定义好的,而非 PHP 自定义的。

消息通讯样例

为了简单的表示,消息头只显示消息的类型和消息的 id,其他字段都不予以显示。下面的例子来自于官网

 

 

{FCGI_BEGIN_REQUEST,   1, {FCGI_RESPONDER, 0}}
{FCGI_PARAMS,          1, "\013\002SERVER_PORT80\013\016SERVER_ADDR199.170.183.42 ... "}
{FCGI_STDIN,           1, "quantity=100&item=3047936"}
{FCGI_STDOUT,          1, "Content-type: text/html\r\n\r\n<html>\n<head> ... "}
{FCGI_END_REQUEST,     1, {0, FCGI_REQUEST_COMPLETE}}

 

 

配合上面各个结构体,则可以大致想到 FastCGI 响应器的解析和响应流程:

首先读取消息头,得到其类型为FCGI_BEGIN_REQUEST,然后解析其消息体,得知其需要的角色就是FCGI_RESPONDERflag为0,表示请求结束后关闭线路。然后解析第二段消息,得知其消息类型为FCGI_PARAMS,然后直接将消息体里的内容以回车符切割后存入环境变量。与之类似,处理完毕之后,则返回了FCGI_STDOUT消息体和FCGI_END_REQUEST消息体供 Web 服务器解析。

 

参考 https://yq.aliyun.com/articles/58999

 

http://redfoxli.github.io/fast-cgi-protocol.html 更细致

 

posted @ 2019-03-22 11:13  taek  阅读(429)  评论(0编辑  收藏  举报