摘要:可以通過等方式按照協議通信。函數所需的變量在進入該函數之前認為已經初始化完成。和都有自己的,且互不干涉,后續發送的序列號以此為基準。
運營研發團隊 方波 施洪寶
一. FastCGI協議簡介 1.1 簡介FastCGI(Fast Common Gateway Interface, 快速通用網關接口)是一種通信協議。可以通過Unix Domain Socket, Named Pipe, TCP等方式按照FastCGI協議通信。
圖 1.1 FastCGI簡介
1.2 數據包格式FastCGI數據包兩部分, 頭部(header), 包體(body), 每個數據包都必須包含header, body可以沒有。header為8個字節, body必須為8的整數倍, 不是的話需要填充。
1.2.1 頭部typedef struct { unsigned char version; // 版本號 unsigned char type; // 數據包類型 unsigned char requestIdB1; // 記錄id高8位 unsigned char requestIdB0; // 記錄id低8位 unsigned char contentLengthB1; // 記錄內容長度高8位(body長度高8位) unsigned char contentLengthB0; // 記錄內容長度低8位(body長度低8位) unsigned char paddingLength; // 補齊位長度(body補齊長度) unsigned char reserved; // 補齊位 }Header;
圖 1.2 FastCGI協議數據包頭部字段說明
type的取值
#define FCGI_BEGIN_REQUEST 1 //(web->fastcgi)請求開始數據包 #define FCGI_ABORT_REQUEST 2 //(web->fastcgi)終止請求 #define FCGI_END_REQUEST 3 //(fastcgi->web)請求結束 #define FCGI_PARAMS 4 //(web->fastcgi)傳遞參數 #define FCGI_STDIN 5 //(web->fastcgi)數據流傳輸數據 #define FCGI_STDOUT 6 //(fastcgi->web)數據流傳輸數據 #define FCGI_STDERR 7 //(fastcgi->web)數據流傳輸 #define FCGI_DATA 8 //(web->fastcgi)數據流傳輸 #define FCGI_GET_VALUES 9 //(web->fastcgi)查詢fastcgi服務器性能參數 #define FCGI_GET_VALUES_RESULT 10 //(fastcgi->web)fastcgi性能參數查詢返回 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)1.2.2 params類型數據包
圖 1.3 Params數據包
說明:
1.params數據包以key, value格式發送, 具體格式為(keyLen, valLen, key, val)
2.key或者val長度大于127時,會用4個字節存儲長度,否則用一個字節
1.2.3 數據流類型數據包(stdin, stdout, stderr, data)圖 1.4 數據流類型數據包
1.3 通信流程示例圖 1.5 FastCGI簡單通信流程
說明:
begin request 代表請求開始, end request 代表請求結束。
除begin request, end request類型數據包外, 其他類型數據包在發送完成后,需要發送一個只有頭部,包體長度為0, 也就是沒有包體的數據包,代表這種類型的數據包發送結束。
1.4 參考https://segmentfault.com/a/11...
https://blog.csdn.net/hepangd...
nginx發送的緩沖區數據格式如下:
圖 2.1 FastCGI數據包總體結構圖
說明:
本部分主要依據ngx_http_fastcgi_create_request函數, 該函數會構造緩存區,并向其中寫入上圖所示內容,上述沒有考慮HTTP請求含有body的情況。
ngx_http_fastcgi_create_request 函數所需的變量, 在進入該函數之前認為已經初始化完成。
2.1 基礎 2.1.1 le.ip結構圖fastcgi le.ip
圖 2.2 le.ip 結構圖
2.1.2 e.ip結構圖圖2.3 e.ip結構圖
2.2 ngx_http_fastcgi_create_request該函數主要依據圖2.1, 將所需數據寫入到ngx_http_request_t對應的ngx_http_upstream_t的緩沖區中。
寫入key, val時, 通過調用相應函數實現, 該函數是與對應的key, val放置在一起的, 如圖2.3所示。
//params數據包寫入核心代碼 while (*(uintptr_t *) le.ip) { lcode = *(ngx_http_script_len_code_pt *) le.ip; key_len = (u_char) lcode(&le); //獲取key的長度 lcode = *(ngx_http_script_len_code_pt *) le.ip; skip_empty = lcode(&le); //查看空時是否跳過 for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) { lcode = *(ngx_http_script_len_code_pt *) le.ip; } le.ip += sizeof(uintptr_t); //當前key:value結束, le.ip后移1位 if (skip_empty && val_len == 0) { //value為空, 并且設置空時跳過該key e.skip = 1; while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); } e.ip += sizeof(uintptr_t); e.skip = 0; continue; } *e.pos++ = (u_char) key_len; //寫入key len if (val_len > 127) { //寫入value len *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80); *e.pos++ = (u_char) ((val_len >> 16) & 0xff); *e.pos++ = (u_char) ((val_len >> 8) & 0xff); *e.pos++ = (u_char) (val_len & 0xff); } else { *e.pos++ = (u_char) val_len; } while (*(uintptr_t *) e.ip) { code = *(ngx_http_script_code_pt *) e.ip; code((ngx_http_script_engine_t *) &e); //調用code存儲的處理函數, 負責將key, value內容寫入緩存, 并將e.ip后移 } e.ip += sizeof(uintptr_t); //當前Key:Value結束, 跳過NULL, e.ip后移一位 ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "fastcgi param: "%*s: %*s"", key_len, e.pos - (key_len + val_len), val_len, e.pos - val_len); }三. 抓包分析 3.1 TCP三次握手
14:50:02.836252 IP bogon.46288 > localhost.cslistener: Flags [S], seq 304127093, win 29200, options [mss 1460,sackOK,TS val 105743206 ecr 0,nop,wscale 7], length 0 14:50:02.874743 IP localhost.cslistener > bogon.46288: Flags [S.], seq 15154, ack 304127094, win 32768, options [mss 1460], length 0 14:50:02.874804 IP bogon.46288 > localhost.cslistener: Flags [.], ack 15155, win 29200, length 0
代碼3.1 tcpdump三次握手
圖3.1 三次握手鏈接過程
Client發送請求包seq 304127093,Server返回確認數據包ack=seq(client)+1,同時返回Server自己的seq 15154,Client收到后發送確認包ack=seq(server)+1,建立鏈接。
Client和Server都有自己的seq,且互不干涉,后續發送的序列號以此為基準。
3.2 請求數據包圖2.1 Client向Server發送請求數據包
Mac幀頭部(14字節)
IP頭部(20字節)
TCP頭部(20字節)
FastCGI數據包(begin request(8+ 8 = 16) + params(8 + 507 + 5 = 520) + end params(8+ 0 = 8) + stdin(8 + 0 = 8) = 552)
Params 數據包參數整理:
key len | val len | key | val | |
---|---|---|---|---|
10 | 4 | PRODUCTION | true | |
15 | 38 | SCRIPT_FILENAME | /home/xiaoju/webroot/default/index.php | |
12 | 0 | QUERY_STRING | ||
14 | 3 | REQUEST_METHOD | GET | |
12 | 0 | CONTENT_TYPE | ||
14 | 0 | CONTENT_LENGTH | ||
11 | 10 | SCRIPT_NAME | /index.php | |
11 | 1 | REQUEST_URI | / | |
12 | 10 | DOCUMENT_URI | /index.php | |
13 | 28 | DOCUMENT_ROOT | /home/xiaoju/webroot/default | |
15 | 8 | SERVER_PROTOCOL | HTTP/1.1 | |
17 | 7 | GATEWAY_INTERFACE | CGI/1.1 | |
15 | 11 | SERVER_SOFTWARE | nginx/1.6.2 | |
11 | 9 | REMOTE_ADDR | 127.0.0.1 | |
11 | 5 | REMOTE_PORT | 42282 | |
11 | 9 | SERVER_ADDR | 127.0.0.1 | |
11 | 4 | SERVER_PORT | 8100 | |
11 | 0 | SERVER_NAME | ||
15 | 3 | REDIRECT_STATUS | 200 | |
15 | 11 | HTTP_USER_AGENT | curl/7.29.0 | |
9 | 14 | HTTP_HOST | localhost:8100 | |
11 | 3 | HTTP_ACCEPT | */* |
說明
stdin類型數據包長度大于32k時, nginx fastcgi會進行分包, 此時會發送多個stdin類型數據包, 發送完成后再發送stdin結束包(只有包頭,沒有包體的數據包)。
wireshark或者tcpdump抓包時, 可能會出現某個數據包長度大于MTU, 主要是由于主機開啟TSO功能導致, 具體可以參考http://wsfdl.com/%E8%B8%A9%E5...
TSO進行的是TCP分段, 不是IP分片
如果抓到的包, IP頭部, identified field is 0, 表明tcp不需分段, don"t fragment 置位1, 具體參考http://www.linuxsa.org.au/pip...
TCP頭部可選項含義參考https://www.jianshu.com/p/39b...
3.3 響應包14:50:02.913289 IP localhost.cslistener > bogon.46288: Flags [P.], seq 15155:15395, ack 304127646, win 32216, length 240 0x0000: 0800 2701 5190 5254 0012 3500 0800 4500 ..".Q.RT..5...E. 0x0010: 0118 02bf 0000 ff06 2fb6 0a60 7207 0a00 ......../..`r... 0x0020: 0204 2328 b4d0 0000 3b33 1220 9e9e 5018 ..#(....;3....P. 0x0030: 7dd8 df40 0000 0106 0001 00d8 0000 5365 }..@..........Se 0x0040: 742d 436f 6f6b 6965 3a20 5048 5053 4553 t-Cookie:.PHPSES 0x0050: 5349 443d 6268 3174 3772 6e61 3233 716c SID=bh1t7rna23ql 0x0060: 6d63 6235 6d6a 686d 3967 756f 7631 3b20 mcb5mjhm9guov1;. 0x0070: 7061 7468 3d2f 0d0a 4578 7069 7265 733a path=/..Expires: 0x0080: 2054 6875 2c20 3139 204e 6f76 2031 3938 .Thu,.19.Nov.198 0x0090: 3120 3038 3a35 323a 3030 2047 4d54 0d0a 1.08:52:00.GMT.. 0x00a0: 4361 6368 652d 436f 6e74 726f 6c3a 206e Cache-Control:.n 0x00b0: 6f2d 7374 6f72 652c 206e 6f2d 6361 6368 o-store,.no-cach 0x00c0: 652c 206d 7573 742d 7265 7661 6c69 6461 e,.must-revalida 0x00d0: 7465 0d0a 5072 6167 6d61 3a20 6e6f 2d63 te..Pragma:.no-c 0x00e0: 6163 6865 0d0a 436f 6e74 656e 742d 7479 ache..Content-ty 0x00f0: 7065 3a20 7465 7874 2f68 746d 6c3b 2063 pe:.text/html;.c 0x0100: 6861 7273 6574 3d55 5446 2d38 0d0a 0d0a harset=UTF-8.... 0x0110: 646f 636b 6572 0103 0001 0008 0000 0000 docker.......... 0x0120: 0000 0064 223a ...d":
代碼3.2 Server向Client發送響應數據包
Mac幀頭部(14字節)
IP頭部(20字節)
TCP頭部(20字節)
FastCGI數據包(begin stdout(8+ 216 = 224) + end request(8+ 8 = 16) = 240)
3.4 Client發送接收Server數據的確認包14:50:02.913365 IP bogon.46288 > localhost.cslistener: Flags [.], ack 15395, win 30016, length 0
圖3.3 數據發送
3.5 斷開鏈接14:50:02.913629 IP bogon.46288 > localhost.cslistener: Flags [F.], seq 304127646, ack 15395, win 30016, length 0 14:50:02.913767 IP localhost.cslistener > bogon.46288: Flags [.], ack 304127647, win 32215, length 0 14:50:02.951270 IP localhost.cslistener > bogon.46288: Flags [F.], seq 15395, ack 304127647, win 32215, length 0 14:50:02.951452 IP bogon.46288 > localhost.cslistener: Flags [.], ack 15396, win 30016, length 0
代碼3.3 tcp斷開鏈接
圖3.4 tcpdump抓包對應的tcp流程圖
3.6 參考https://my.oschina.net/manmao...
https://segmentfault.com/a/11...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/40175.html
摘要:可以通過等方式按照協議通信。上述都需要發送結束包。函數所需的變量在進入該函數之前認為已經初始化完成。和都有自己的,且互不干涉,后續發送的序列號以此為基準。 運營研發團隊 施洪寶 一. FastCGI協議簡介 1.1 簡介 FastCGI(Fast Common Gateway Interface, 快速通用網關接口)是一種通信協議。可以通過Unix Domain Socket, Na...
摘要:分析的結果,發現內存,基本沒有什么大的變化,網卡流量明顯降低,上下文切換明顯升高。網卡流量降低可以理解,因為當前系統已不能正常返回響應,但上下文切換升高卻不知道什么原因。 原文:http://chuansongme.com/n/797172 背景 據XX部門兄弟反應, 其在將PHP從5.3.8 升級到5.5.13 時, 開始運行正常, 運行一段時間后, 系統負載變高,達到200%以...
摘要:所以,它就會將端口號還有一些額外的信息被稱作首部,和應用層下發的數據部分進行封裝,一起傳給下一層即網絡層。它們之間的通信,屬于同一機器上不同端口號之間的通信。而協議傳輸的僅僅是無意義的字節流數據,接收方并不能正確讀取數據的含義。 baiyan 全部視頻:https://segmentfault.com/a/11... 計算機網絡架構的分層與封裝 我們經常談到,計算機網絡有多種體系架構...
閱讀 3088·2021-09-22 15:20
閱讀 2608·2019-08-30 15:54
閱讀 1973·2019-08-30 14:06
閱讀 3122·2019-08-30 13:05
閱讀 2467·2019-08-29 18:36
閱讀 578·2019-08-29 15:10
閱讀 533·2019-08-29 11:17
閱讀 830·2019-08-28 18:11