小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

古老的CGI與Web開(kāi)發(fā)

 nxhujiee 2018-07-17

C++后臺(tái)實(shí)踐:古老的CGI與Web開(kāi)發(fā)

本文寫給C/C++程序猿,也適合其他對(duì)歷史感興趣的程序猿

=============================================

        談到web開(kāi)發(fā),大家首先想到的PHP、JavaEE/JSP、.NET/ASP、Ruby on rails、Python的Django等等??芍^百花齊放,你一般不會(huì)想到C++和Web開(kāi)發(fā)有什么關(guān)系,但其實(shí)動(dòng)態(tài)網(wǎng)頁(yè)的開(kāi)發(fā)(web開(kāi)發(fā))可是在這些動(dòng)態(tài)網(wǎng)頁(yè)語(yǔ)言誕生之前就存在了的。所以C/C++也是可以做web開(kāi)發(fā)的,它利用的技術(shù)是——CGI。

        在天地初開(kāi),混沌未分之時(shí),動(dòng)態(tài)網(wǎng)頁(yè)語(yǔ)言尚未出世,要實(shí)現(xiàn)動(dòng)態(tài)網(wǎng)站依賴的就是CGI。谷歌/百度一下CGI,可能會(huì)出現(xiàn)很多名詞:CGI腳本、CGI程序、CGI標(biāo)準(zhǔn)等等。其實(shí)這些都是站在不同角度來(lái)說(shuō)的,CGI即Common Gateway Interface的縮寫,直譯為“通用網(wǎng)關(guān)接口”。第一次聽(tīng)這個(gè)名字,我也不知道是個(gè)什么鬼東西。歸根結(jié)底 CGI就是一個(gè)接口協(xié)議。協(xié)議就是大家公認(rèn)的一套標(biāo)準(zhǔn)(叫CGI標(biāo)準(zhǔn)也可以),比如網(wǎng)絡(luò)協(xié)議。大家都遵守一套標(biāo)準(zhǔn),就減少了溝通的難度。進(jìn)行CGI開(kāi)發(fā),就是編寫一個(gè)CGI可執(zhí)行程序。其實(shí)各種語(yǔ)言都可以編寫CGI,不但Java、Python、PHP、C#……可以,而且Shell也可以。當(dāng)然C和C++也可以。由于早期CGI很多是由Perl(腳本語(yǔ)言)開(kāi)發(fā)的,所以CGI程序也稱CGI腳本,其實(shí)這個(gè)稱呼不一定準(zhǔn)確。因?yàn)镃++編譯出的可執(zhí)行文件同樣可以是CGI。

        在PHP和Java大行其道的今天,很多人看來(lái)用C++編寫CGI是幾乎淘汰的技術(shù)了(其實(shí)這到不然,只是比較小眾罷了)。所以如果你對(duì)C/C++感興趣或者對(duì)歷史感興趣都可以閱讀本文。

一次網(wǎng)頁(yè)請(qǐng)求與響應(yīng)

        在進(jìn)行網(wǎng)頁(yè)瀏覽時(shí),通常就是通過(guò)一個(gè)URL請(qǐng)求一個(gè)網(wǎng)頁(yè),然后服務(wù)器返回這個(gè)網(wǎng)頁(yè)文件給瀏覽器。瀏覽器在本地解析該文件渲染成我們看到的網(wǎng)頁(yè)。然而通常我們看到的網(wǎng)頁(yè)不是靜態(tài)網(wǎng)頁(yè),也就是說(shuō)在服務(wù)端是沒(méi)有這個(gè)網(wǎng)頁(yè)文件,它是在網(wǎng)頁(yè)請(qǐng)求的時(shí)候動(dòng)態(tài)生成的,比如PHP/JSP網(wǎng)頁(yè)。依據(jù)你請(qǐng)求的參數(shù)不同,所返回的內(nèi)容不同。
        同理,如果是請(qǐng)求一個(gè)CGI程序的時(shí)候(比如在瀏覽器直接輸入CGI程序的URL,或者提交表單的時(shí)候發(fā)送給CGI程序),CGI程序負(fù)責(zé)解析從前端傳遞過(guò)來(lái)的參數(shù),理解它的意圖然后返回?cái)?shù)據(jù),比如返回HTML、XML或JSON等。
WARNNING:Apache默認(rèn)沒(méi)有打開(kāi)CGI的支持,需要進(jìn)行CGI的配置。具體方法可以自行百度。

預(yù)備前端知識(shí)

        假設(shè)你是一個(gè)C++程序員,你可能對(duì)前端不熟(OK,我也不熟),在接下來(lái)的講述之前,你要先掌握一些預(yù)備的前端知識(shí)(盡量少講前端),你不需要知道如何渲染出一個(gè)美輪美奐的網(wǎng)頁(yè),但你需要知道前、后端如何交互。前端頁(yè)面如何發(fā)送數(shù)據(jù),一個(gè)普通的HTML頁(yè)面通常的做法,你只需知道如下幾種:
  • form表單提交(html原生)
  • js操縱下的表單提交
  • js通過(guò)Ajax請(qǐng)求數(shù)據(jù)
這里知講第一種(最簡(jiǎn)單的):
[html] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. <h1>表單提交</h1>  
  2. <form action="/cgi-bin/hello.cgi" method="get">  
  3. <table>  
  4.     <tr>  
  5.         <td>用戶名:</td>  
  6.         <td><input name="username"/></td>  
  7.     </tr>  
  8.         <tr>  
  9.         <td>密碼:</td>  
  10.         <td><input name="password"/></td>  
  11.     </tr>  
  12.     <tr>  
  13.         <td><input type="submit" value="OK"/></td>  
  14.     </tr>  
  15.     </table>  
  16. </form>  


form標(biāo)簽的action屬性的值表示的就是表單要提交到url,即表單提交以后要跳轉(zhuǎn)的頁(yè)面(Ajax可以達(dá)到無(wú)跳轉(zhuǎn)拉取數(shù)據(jù),刷新頁(yè)面),這里action屬性值的是cgi程序的url地址。(WARNNING:/ 對(duì)應(yīng)的是網(wǎng)站根目錄,而不是Linux文件系統(tǒng)根目錄哦)。method屬性表示數(shù)據(jù)請(qǐng)求方式,有兩種:get和post。不贅述。

        我輸入用戶名jellywang,密碼123456之后,點(diǎn)擊OK按鈕,即向 當(dāng)前域名/cgi-bin/hello.cgi 的程序序提交了表單,并且攜帶參數(shù)username=jellywang。然后頁(yè)面會(huì)跳轉(zhuǎn)到這個(gè)cgi(就像普通網(wǎng)頁(yè)跳轉(zhuǎn),瀏覽器地址欄更新一樣)。
        如果是get請(qǐng)求。那么瀏覽器地址欄的URL看起來(lái)像這樣:localhost:/cgi-bin/hello.cgi?username=jelly&password=123456。很顯然這是一種不夠安全的方式,所以我們還可以使用post請(qǐng)求。這樣地址欄就看不到這種提交的參數(shù)了。(其實(shí)post也不夠安全,不鼓勵(lì)直接提交明文密碼的方式,本文僅作示例,安全登錄不上本文重點(diǎn))

環(huán)境變量與CGI處理

        當(dāng)前端頁(yè)面通過(guò)get或post方法向cgi程序提交了數(shù)據(jù)以后,那么接下來(lái)cgi程序該如何解析呢?答案是環(huán)境變量。無(wú)論是Linux系統(tǒng)或Windows系統(tǒng)都有環(huán)境變量的概念。Linux用戶在配置很多環(huán)境的時(shí)候,都不得不在系統(tǒng)配置文件中和環(huán)境變量打交道。CGI程序即是通過(guò)從環(huán)境變量中取值來(lái)獲得參數(shù)的。這里介紹幾個(gè)環(huán)境變量(更多的請(qǐng)自行百度):

REQUEST_METHOD

前端頁(yè)面數(shù)據(jù)請(qǐng)求方式:get/post

QUERY_STRING

采用GET時(shí)所傳輸?shù)男畔?/p>

CONTENT_LENGTH

STDIO中的有效信息長(zhǎng)度

SCRIPT_NAME

所調(diào)用的CGI程序的名字

SERVER_NAME

服務(wù)器的IP或名字

SERVER_PORT

主機(jī)的端口號(hào)


        這些環(huán)境變量是從何而來(lái),是誰(shuí)定義的?是Linux嗎?POSIX嗎?當(dāng)然不是。這里就要再次聲明一下CGI是一個(gè)接口協(xié)議,這些環(huán)境變量就是屬于該協(xié)議的內(nèi)容,所以不論你的server所在的操作系統(tǒng)是Linux還是Windows,也不論你的server是Apache還是Nginx,這些變量的名稱和含義都是一樣的。實(shí)際就是Apache/Nginx在將這些內(nèi)容填充到環(huán)境變量中,而具體填充規(guī)范則來(lái)自于CGI接口協(xié)議。
        在C語(yǔ)言標(biāo)準(zhǔn)中有獲取環(huán)境變量值得庫(kù)函數(shù)——getenv。(頭文件stdlib.h)
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. //比如  
  2. chr* str = NULL;  
  3. str = getenv("QUERY_STRING");  
        對(duì)于get請(qǐng)求,可以從環(huán)境變量QUERY_STRING中取出字符串 username=jelly&password=123456。然后程序自己做字符串的解析操作,解析出參數(shù)的key和value。而對(duì)于post請(qǐng)求,則是直接通過(guò)標(biāo)注輸入(STDIN)來(lái)獲取這個(gè)參數(shù)字符串,比如使用scanf或cin都可以。
        在解析了請(qǐng)求、進(jìn)行了相應(yīng)的邏輯處理之后(比如檢查用戶名密碼是否一致),CGI程序要向前端頁(yè)面返回內(nèi)容,這是通過(guò)標(biāo)準(zhǔn)輸出(STDOUT)完成的,比如printf或cout,你可以返回xml,json,plain text或一個(gè)html網(wǎng)頁(yè)等等。這一步完成的是就是HTTP的響應(yīng)過(guò)程。所以在返回直接的數(shù)據(jù)之前,要先輸出HTTP協(xié)議的首部。比如,假設(shè)你想返回一個(gè)html網(wǎng)頁(yè),那么你首先要輸出:
[cpp] view plain copy
 在CODE上查看代碼片派生到我的代碼片
  1. cout<<"Content-Type:text/html\n\n"<<endl;  
WARNNING:這里要注意,一定要輸出兩個(gè)換行符(\n)。因?yàn)镠TTP協(xié)議的首部和消息實(shí)體(如HTML代碼)之間用空行分割。
        后面直接cout出html代碼(比如輸出你剛才輸入的用戶名成功登陸)。前端頁(yè)面就會(huì)收到這些html代碼,然后瀏覽器就渲染成網(wǎng)頁(yè)啦。這就是一次CGI完成的動(dòng)態(tài)網(wǎng)頁(yè)操作了。 

Cgicc庫(kù)

        進(jìn)行C++的CGI編程,需要手動(dòng)進(jìn)行字符串的解析處理,還有自行管理首部。比如資源轉(zhuǎn)移了,要返回302,并且在首部用Location給出新地址。很顯然,這些東西對(duì)于PHP、Python等語(yǔ)言都有內(nèi)置的解決方案。對(duì)于C++就需要第三方庫(kù)了。這里推薦一個(gè)GNU的開(kāi)源庫(kù)——Cgicc??梢詽M足常用的各類需求,除了解析get/post請(qǐng)求外,還能重定向,還可以設(shè)置Cookie,還可以上傳文件等等等等。
        美中不足的就是Cgicc庫(kù)不支持SESSION。但是這個(gè)問(wèn)題不大,我們可以很容易使用Cookie來(lái)實(shí)現(xiàn)SESSION功能。由于CGI本身是請(qǐng)求一次就創(chuàng)建一個(gè)進(jìn)程,返回之后進(jìn)程就結(jié)束(下文的FastCGI除外)。這時(shí)要在服務(wù)端維持一個(gè)SESSION的變量可選的解決方案是:用文件存儲(chǔ)或者在Redis、Memcached等內(nèi)存數(shù)據(jù)庫(kù)中存儲(chǔ)。而發(fā)給客戶端的SESSIONID就用Cgicc已經(jīng)支持的Cookie功能來(lái)完成,就可以了。

CGI的痛點(diǎn)與FastCGI

        CGI是一種標(biāo)準(zhǔn),并不限定語(yǔ)言。所以Java、PHP、Python都可以通過(guò)這種方式來(lái)生成動(dòng)態(tài)網(wǎng)頁(yè)。但是實(shí)際上這些動(dòng)態(tài)語(yǔ)言卻很少這樣用。原來(lái)是CGI有一大硬傷。那就是每次CGI請(qǐng)求,那么Apache都有啟動(dòng)一個(gè)進(jìn)程去執(zhí)行這個(gè)CGI程序,即頗具Unix特色的fork-and-execute。當(dāng)用戶請(qǐng)求量大的時(shí)候,這個(gè)fork-and-execute的操作會(huì)嚴(yán)重拖慢Server的進(jìn)程。而Java的Servlet技術(shù)則是一種常駐內(nèi)存的技術(shù),不會(huì)頻繁的發(fā)生進(jìn)程上下文的創(chuàng)建和銷毀操作。
        時(shí)勢(shì)造英雄,F(xiàn)astCGI技術(shù)應(yīng)運(yùn)而生。簡(jiǎn)單來(lái)說(shuō),其本質(zhì)就是一個(gè)常駐內(nèi)存的進(jìn)程池技術(shù),由調(diào)度器負(fù)責(zé)將傳遞過(guò)來(lái)的CGI請(qǐng)求發(fā)送給處理CGI的handler進(jìn)程來(lái)處理。在一個(gè)請(qǐng)求處理完成之后,該處理進(jìn)程不銷毀,繼續(xù)等待下一個(gè)請(qǐng)求的到來(lái)。FCGI技術(shù)一出,CGI又一定程度上煥發(fā)了第二春。PHP-FPM本身是使PHP支持FCGI技術(shù)的一個(gè)Patch,現(xiàn)在已經(jīng)被納入PHP標(biāo)準(zhǔn)。當(dāng)然,支持C++的FCGI技術(shù)也出現(xiàn)了,Apache有FCGI的模塊可以安裝,比如mod_fcgid。

現(xiàn)代CGI的編程范式

        前面我們知道,CGI可以直接返回一個(gè)html網(wǎng)頁(yè)。CGI程序本身也可以進(jìn)行各種計(jì)算、邏輯處理任務(wù)。隨著各類web前后端技術(shù)的發(fā)展,以及大數(shù)據(jù)、高并發(fā)的Server使用場(chǎng)景越來(lái)越多?,F(xiàn)代的CGI的用法,在發(fā)生變化。
        現(xiàn)在,越來(lái)越多的任務(wù)從后端轉(zhuǎn)移到前端,前端頁(yè)面利用豐富的Js技術(shù)來(lái)進(jìn)行更多的處理。
  1. JS可以使用Ajax技術(shù)來(lái)向后臺(tái)CGI發(fā)起數(shù)據(jù)請(qǐng)求。Ajax完成的是不需要刷新整個(gè)頁(yè)面就可以加載后端數(shù)據(jù)(比如從數(shù)據(jù)庫(kù)中取出)。
  2. CGI一般不再用于直接返回html頁(yè)面,同時(shí)將復(fù)雜的計(jì)算、IO任務(wù)下沉到后端(后端可以進(jìn)一步進(jìn)行路由轉(zhuǎn)發(fā),實(shí)現(xiàn)負(fù)載均衡)。使CGI作為前后端之間的中間層。彼時(shí)CGI的職能是完成基本的數(shù)據(jù)交換:解析前端數(shù)據(jù)請(qǐng)求,再轉(zhuǎn)發(fā)給對(duì)應(yīng)后端;然后從后端取回?cái)?shù)據(jù),給前端返回XML或JSON。
  3. 前端JS利用XML/JSON中的數(shù)據(jù)來(lái)進(jìn)行填充,繪制出豐富的頁(yè)面。
上一篇CGI程序中POST和GET消息的處理

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多