fastcgi

nginx和php-fpm是經過fastcgi協議進行通訊的,php-fpm自己就是個socket服務端,nginx拿到url後,識別出.php結尾的地址後,會傳給php-fpm,php-fpm經過預先啓動的進程,調用php解釋器php

fastcgi消息有10種,其中有html

fcgi_begin_requestnginx

fcgi_end_requestgit

fcgi_params //環境變量,QUERY_STRING get後面的數據github

fcgi_stdin //POST數據數據庫

fcgi_stdout //php-fpm返回給nginx的數據服務器

fcgi_params key value格式 name長度value長度namevaluemarkdown

長度小於128,使用一個字節併發

長度大於等於128,使用四個字節app

首字節的第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 更細緻

相關文章
相關標籤/搜索