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
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;
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_status
和fcgi_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_RESPONDER
,flag
爲0,表示請求結束後關閉線路。而後解析第二段消息,得知其消息類型爲FCGI_PARAMS
,而後直接將消息體裏的內容以回車符切割後存入環境變量。與之相似,處理完畢以後,則返回了FCGI_STDOUT
消息體和FCGI_END_REQUEST
消息體供 Web 服務器解析。
參考 https://yq.aliyun.com/articles/58999
http://redfoxli.github.io/fast-cgi-protocol.html 更細緻