欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何用C寫(xiě)一個(gè)web服務(wù)器之CGI協(xié)議

 更新時(shí)間:2021年05月27日 10:11:47   作者:枕邊書(shū)  
本文主要介紹了如何用C寫(xiě)一個(gè)web服務(wù)器之CGI協(xié)議,對(duì)C語(yǔ)言和web感興趣的同學(xué),可以詳細(xì)看下,并且試驗(yàn)一下。

前言

這次更新主要實(shí)現(xiàn)一下 CGI 協(xié)議。

先放上GitHub鏈接https://github.com/zhenbianshu/tinyServer

作為一個(gè)服務(wù)器,基本要求是能受理請(qǐng)求,提取信息并將消息分發(fā)給 CGI 解釋器,再將解釋器響應(yīng)的消息包裝后返回客戶端。在這個(gè)過(guò)程中,除了和客戶端 socket 之間的交互,還要牽扯到第三個(gè)實(shí)體 - 請(qǐng)求解釋器。

如上圖所示,客戶端負(fù)責(zé)封裝請(qǐng)求和解析響應(yīng),服務(wù)器的主要職責(zé)是管理連接、數(shù)據(jù)轉(zhuǎn)換、傳輸和分發(fā)客戶端請(qǐng)求,而真正進(jìn)行數(shù)據(jù)文檔處理與數(shù)據(jù)庫(kù)操作的就是請(qǐng)求解釋器,這個(gè)解釋器,在 PHP 中一般是 PHP-FPM,JAVA 中是 Servlet。

我們之前進(jìn)行的處理多在客戶端和服務(wù)器之間的通信,以及服務(wù)器的內(nèi)部調(diào)整,這次更新的內(nèi)容主要是后面兩個(gè)實(shí)體之間的進(jìn)程間通信。

進(jìn)程間通信牽涉到三個(gè)方面,即方式和形式和內(nèi)容。

方式指的是進(jìn)程間通信的傳輸媒介,如 Nginx 中實(shí)現(xiàn)的 TCP 方式和 Unix Domain Socket,它們分別有跨機(jī)器和高效率的優(yōu)點(diǎn),還有我實(shí)現(xiàn)的服務(wù)器用了很 low 的popen方式。

而形式就是數(shù)據(jù)格式了,我認(rèn)為它并無(wú)定式,只要服務(wù)器容易組織數(shù)據(jù),解釋器能方便地接收并解析,最好也能節(jié)約傳輸資源,提高傳輸效率。目前的解決方案有經(jīng)典的 xml,輕巧易理解的 json 和谷歌高效率的 protobuf。它們各有優(yōu)點(diǎn),我選擇了 json,主要是因?yàn)橛蠧Json庫(kù)的存在,數(shù)據(jù)在 C 中方便組織,而在PHP中,一個(gè)json_decode()方法就完成了數(shù)據(jù)解析。

至于應(yīng)該傳輸哪些內(nèi)容呢?CGI 描述了一套協(xié)議:

CGI

通用網(wǎng)關(guān)接口(Common Gateway Interface/CGI)是一種重要的互聯(lián)網(wǎng)技術(shù),可以讓一個(gè)客戶端,從網(wǎng)頁(yè)瀏覽器向執(zhí)行在網(wǎng)絡(luò)服務(wù)器上的程序請(qǐng)求數(shù)據(jù)。CGI描述了服務(wù)器和請(qǐng)求處理程序之間傳輸數(shù)據(jù)的一種標(biāo)準(zhǔn)。

CGI 是服務(wù)器與解釋器交互的接口,服務(wù)器負(fù)責(zé)受理請(qǐng)求,并將請(qǐng)求信息解釋為一條條基本的請(qǐng)求信息(在文檔中被稱(chēng)為“元數(shù)據(jù)”),傳送給解釋器來(lái)解釋執(zhí)行,而解釋器響應(yīng)文檔和數(shù)據(jù)庫(kù)操作信息。

之前看了一下 CGI 的 RFC 文檔,總結(jié)了幾個(gè)重要點(diǎn),有興趣的可以看下底部參考文獻(xiàn)。常見(jiàn)規(guī)范(信息太多,只考慮 MUST 的情況)如下:

CGI請(qǐng)求

  • 服務(wù)器根據(jù) 以 / 分隔的路徑選擇解釋器;
  • 如果有 AUTH 字段,需要先執(zhí)行 AUTH,再執(zhí)行解釋器;
  • 服務(wù)器確認(rèn) CONTENT-LENGTH 表示的是數(shù)據(jù)解析出來(lái)的長(zhǎng)度,如果附帶信息體,則必須將長(zhǎng)度字段傳送到解釋器;
  • 如果有 CONTENT-TYPE 字段,服務(wù)器必須將其傳給解釋器;若無(wú)此字段,但有信息體,則服務(wù)器判斷此類(lèi)型或拋棄信息體;
  • 服務(wù)器必須設(shè)置 QUERY_STRING 字段,如果客戶端沒(méi)有設(shè)置,服務(wù)端要傳一個(gè)空字符串“”
  • 服務(wù)器必須設(shè)置 REMOTE_ADDR,即客戶端請(qǐng)求IP;
  • REQUEST_METHOD 字段必須設(shè)置, GET POST 等,大小寫(xiě)敏感;
  • SCRIPT_NAME 表示執(zhí)行的解釋器腳本名,必須設(shè)置;
  • SERVER_NAME 和 SERVER_PORT 代表著大小寫(xiě)敏感的服務(wù)器名和服務(wù)器受理時(shí)的TCP/IP端口;
  • SERVER_PROTOCOL 字段指示著服務(wù)器與解釋器協(xié)商的協(xié)議類(lèi)型,不一定與客戶端請(qǐng)求的SCHEMA 相同,如'https://'可能為HTTP;
  • 在 CONTENT-LENGTH 不為 NULL 時(shí),服務(wù)器要提供信息體,此信息體要嚴(yán)格與長(zhǎng)度相符,即使有更多的可讀信息也不能多傳;
  • 服務(wù)器必須將數(shù)據(jù)壓縮等編碼解析出來(lái);

CGI響應(yīng)

  • CGI解釋器必須響應(yīng) 至少一行頭 + 換行 + 響應(yīng)內(nèi)容;
  • 解釋器在響應(yīng)文檔時(shí),必須要有 CONTENT-TYPE 頭;
  • 在客戶端重定向時(shí),解釋器除了 client-redir-response=絕對(duì)url地址,不能再有其他返回,然后服務(wù)器返回一個(gè) 302 狀態(tài)碼;
  • 解釋器響應(yīng) 三位數(shù)字狀態(tài)碼,具體配置可自行搜索;
  • 服務(wù)器必須將所有解釋器返回的數(shù)據(jù)響應(yīng)給客戶端,除非需要壓縮等編碼,服務(wù)器不能修改響應(yīng)數(shù)據(jù);

Nginx和PHP的CGI實(shí)現(xiàn)

介紹完了 CGI,我們來(lái)參考一下當(dāng)前服務(wù)器 CGI 協(xié)議實(shí)現(xiàn)的成熟方案,這里挑選我熟悉的 Nginx 和 PHP。

在 Nginx 和 PHP 的配合中,Nginx 自然是服務(wù)器,而解釋器是 PHP 的 SAPI。

SAPI

SAPI: Server abstraction API,指的是 PHP 具體應(yīng)用的編程接口,它使得 PHP 可以和其他應(yīng)用進(jìn)行交互數(shù)據(jù)。

PHP 腳本要執(zhí)行可以通過(guò)很多種方式,通過(guò) Web 服務(wù)器,或者直接在命令行下,也可以嵌入在其他程序中。常見(jiàn)的 sapi 有apache2handler、fpm-fcgi、cli、cgi-fcgi,可以通過(guò) PHP 函數(shù)php_sapi_name()來(lái)查看當(dāng)前 PHP 執(zhí)行所使用的 sapi。

PHP5.3 之前使用的與服務(wù)器交互的 sapi 是cgi,它實(shí)現(xiàn)基本的 CGI 協(xié)議,由于它每次處理請(qǐng)求都要?jiǎng)?chuàng)建一個(gè)進(jìn)程、初始化進(jìn)程、處理請(qǐng)求、銷(xiāo)毀進(jìn)程,消耗過(guò)大,使得系統(tǒng)性能大大下降。

這時(shí)候便出現(xiàn)了 CGI 協(xié)議的升級(jí)版本 Fast-CGI。

PHP-FPM

快速通用網(wǎng)關(guān)接口(Fast Common Gateway Interface/FastCGI)是一種讓交互程序與Web服務(wù)器通信的協(xié)議。FastCGI是早期通用網(wǎng)關(guān)接口(CGI)的增強(qiáng)版本。

Fast-CGI 提升效率主要靠將 CGI 解釋器長(zhǎng)駐內(nèi)存重現(xiàn),避免了進(jìn)程反復(fù)加載的損耗。PHP 的 sapi cgi-fcgi實(shí)現(xiàn)了 Fast-CGI 協(xié)議,提升了 PHP 處理 Web 請(qǐng)求的效率。

那么我們常見(jiàn)的 php-fpm 是什么呢?它是一種進(jìn)程管理器(PHP-FastCGI Process Manager),它負(fù)責(zé)管理實(shí)現(xiàn) Fast-CGI 的那些進(jìn)程(worker進(jìn)程),它加載php.ini信息,初始化 worker 進(jìn)程,并實(shí)現(xiàn)平滑重啟和其他高級(jí)功能。

Nginx 將請(qǐng)求都交給 php-fpm,fpm 選擇一個(gè)空閑工作進(jìn)程來(lái)處理請(qǐng)求。

糾偏

這里總結(jié)一下幾個(gè)名字,以防混淆:

  • sapi,是 PHP 與外部進(jìn)程交互的接口;
  • CGI/Fast-CGI(大寫(xiě))是一種協(xié)議;
  • 本節(jié)中出現(xiàn)的 cgi(小寫(xiě)),是指 PHP 的 sapi,即實(shí)現(xiàn) CGI 協(xié)議的一種接口。
  • php-fpm 是管理實(shí)現(xiàn)了Fast-CGI協(xié)議的進(jìn)程的一個(gè)進(jìn)程。

代碼實(shí)現(xiàn)

介紹完了高端的Nginx服務(wù)器,說(shuō)一下我的實(shí)現(xiàn):

服務(wù)器解析 http 報(bào)文,實(shí)現(xiàn) CGI 協(xié)議,將數(shù)據(jù)包裝成 json 格式,通過(guò) PHP 的cli sapi 發(fā)送至 PHP 進(jìn)程,PHP 進(jìn)程解析后響應(yīng) json 格式數(shù)據(jù),服務(wù)器解析響應(yīng)數(shù)據(jù)后包裝成 http 響應(yīng)報(bào)文發(fā)送給客戶端。

http_parser

首要任務(wù)是解析 http 報(bào)文,C 中沒(méi)有很豐富字符串函數(shù),我也沒(méi)有封裝過(guò)常用的函數(shù)庫(kù),所以只好臨時(shí)自己實(shí)現(xiàn)了一個(gè)util_http.c,這里介紹幾個(gè)處理 http 報(bào)文時(shí)好用的字符串函數(shù)。

strtok(char str[], const *delimeter),將 delimeter 設(shè)置為 "\n",分行處理 http 報(bào)文頭正好適合。

sscanf(const *str, format, dest1[,dest...]),它從字符串中以特定格式讀取字符串,讀取時(shí)的分隔符是空格,用它來(lái)處理 http 請(qǐng)求行十分方便。

至于解析 http 報(bào)文頭的鍵值對(duì)應(yīng),沒(méi)想到好方法,只好使用字符遍歷來(lái)判斷。

cJSON

cJSON 是一個(gè) C 實(shí)現(xiàn)的用以生成和解析 json 格式數(shù)據(jù)的函數(shù)庫(kù),在 GitHub 上可以輕松搜到,只用兩個(gè)文件 cJSON.c和cJSON.h即可。

需要注意:C 作為強(qiáng)類(lèi)型語(yǔ)言,往 json 內(nèi)添加不同類(lèi)型的數(shù)據(jù)要使用不同的方法,cJSON 支持 string, bool, number, cJSON object等類(lèi)型。

這里簡(jiǎn)單地介紹一下生成和解析的一般方法;

生成:

cJSON *root; // 聲明cJSON格式數(shù)據(jù)
root = cJSON_CreateObject(); // 創(chuàng)建一個(gè)cJSON對(duì)象
cJSON_AddStringToObject(root, "key", "value") // 往cJSON對(duì)象內(nèi)添加鍵值對(duì)
char *output = cJSON_PrintUnformatted(root); // 生成json字符串
cJSON_Delete(root); // 別忘記釋放內(nèi)存

解析:

cJSON *json = cJSON_Parse(response_json);
value = cJSON_GetObjectItem(cJSON, "key");

當(dāng)然,也可以聲明 cJSON 類(lèi)型的數(shù)據(jù)進(jìn)行嵌套;

以上就是如何用C寫(xiě)一個(gè)web服務(wù)器之CGI協(xié)議的詳細(xì)內(nèi)容,更多關(guān)于用C寫(xiě)一個(gè)web服務(wù)器之CGI協(xié)議的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論