本文寫給C/C++程序猿,也適合其餘對歷史感興趣的程序猿html
=============================================前端
談到web開發,你們首先想到的PHP、JavaEE/JSP、.NET/ASP、Ruby on rails、Python的Django等等。可謂百花齊放,你通常不會想到C++和Web開發有什麼關係,但其實動態網頁的開發(web開發)但是在這些動態網頁語言誕生以前就存在了的。因此C/C++也是能夠作web開發的,它利用的技術是——CGI。程序員
在天地初開,混沌未分之時,動態網頁語言還沒有出世,要實現動態網站依賴的就是CGI。谷歌/百度一下CGI,可能會出現不少名詞:CGI腳本、CGI程序、CGI標準等等。其實這些都是站在不一樣角度來講的,CGI即Common Gateway Interface的縮寫,直譯爲「通用網關接口」。第一次聽這個名字,我也不知道是個什麼鬼東西。歸根結底 CGI就是一個接口協議。協議就是你們公認的一套標準(叫CGI標準也能夠),好比網絡協議。你們都遵照一套標準,就減小了溝通的難度。進行CGI開發,就是編寫一個CGI可執行程序。其實各類語言均可以編寫CGI,不但Java、Python、PHP、C#……能夠,並且Shell也能夠。固然C和C++也能夠。因爲早期CGI不少是由Perl(腳本語言)開發的,因此CGI程序也稱CGI腳本,其實這個稱呼不必定準確。由於C++編譯出的可執行文件一樣能夠是CGI。web
在PHP和Java大行其道的今天,不少人看來用C++編寫CGI是幾乎淘汰的技術了(其實這到否則,只是比較小衆罷了)。因此若是你對C/C++感興趣或者對歷史感興趣均可以閱讀本文。數據庫
一次網頁請求與響應
在進行網頁瀏覽時,一般就是經過一個URL請求一個網頁,而後服務器返回這個網頁文件給瀏覽器。瀏覽器在本地解析該文件渲染成咱們看到的網頁。然而一般咱們看到的網頁不是靜態網頁,也就是說在服務端是沒有這個網頁文件,它是在網頁請求的時候動態生成的,好比PHP/JSP網頁。依據你請求的參數不一樣,所返回的內容不一樣。
同理,若是是請求一個CGI程序的時候(
好比在瀏覽器直接輸入CGI程序的URL,或者提交表單的時候發送給CGI程序),CGI程序負責解析從前端傳遞過來的參數,理解它的意圖而後返回數據,好比返回HTML、XML或JSON等。
WARNNING:Apache默認沒有打開CGI的支持,須要進行CGI的配置。具體方法能夠自行百度。
預備前端知識
假設你是一個C++程序員,你可能對前端不熟(OK,我也不熟),在接下來的講述以前,你要先掌握一些預備的前端知識(儘可能少講前端),你不須要知道如何渲染出一個美輪美奐的網頁,但你須要知道前、後端如何交互。前端頁面如何發送數據,一個普通的HTML頁面一般的作法,你只需知道以下幾種:
- form表單提交(html原生)
- js操縱下的表單提交
- js經過Ajax請求數據
這裏知講第一種(最簡單的):
<h1>表單提交</h1>
<form action="/cgi-bin/hello.cgi" method="get">
<table>
<tr>
<td>用戶名:</td>
<td><input name="username"/></td>
</tr>
<tr>
<td>密碼:</td>
<td><input name="password"/></td>
</tr>
<tr>
<td><input type="submit" value="OK"/></td>
</tr>
</table>
</form>
form標籤的action屬性的值表示的就是表單要提交到url,即表單提交之後要跳轉的頁面(Ajax能夠達到無跳轉拉取數據,刷新頁面),這裏action屬性值的是cgi程序的url地址。(WARNNING:/ 對應的是網站根目錄,而不是Linux文件系統根目錄哦)。method屬性表示數據請求方式,有兩種:get和post。不贅述。
我輸入用戶名jellywang,密碼123456以後,點擊OK按鈕,即向
當前域名/cgi-bin/hello.cgi 的程序序提交了表單,而且攜帶參數username=jellywang。而後頁面會跳轉到這個cgi(就像普通網頁跳轉,瀏覽器地址欄更新同樣)。
若是是get請求。那麼瀏覽器地址欄的URL看起來像這樣:localhost:/cgi-bin/hello.cgi?username=jelly&password=123456。很顯然這是一種不夠安全的方式,因此咱們還可使用post請求。這樣地址欄就看不到這種提交的參數了。(其實post也不夠安全,不鼓勵直接提交明文密碼的方式,本文僅做示例,安全登陸不上本文重點)
環境變量與CGI處理
當前端頁面經過get或post方法向cgi程序提交了數據之後,那麼接下來cgi程序該如何解析呢?答案是
環境變量。不管是Linux系統或Windows系統都有環境變量的概念。Linux用戶在配置不少環境的時候,都不得不在系統配置文件中和環境變量打交道。CGI程序便是經過從環境變量中取值來得到參數的。這裏介紹幾個環境變量(更多的請自行百度):
REQUEST_METHOD編程 |
前端頁面數據請求方式:get/postjson |
QUERY_STRING後端 |
採用GET時所傳輸的信息瀏覽器 |
CONTENT_LENGTH安全 |
STDIO中的有效信息長度 |
SCRIPT_NAME |
所調用的CGI程序的名字 |
SERVER_NAME |
服務器的IP或名字 |
SERVER_PORT |
主機的端口號 |
這些環境變量是從何而來,是誰定義的?是Linux嗎?POSIX嗎?固然不是。這裏就要再次聲明一下CGI是一個接口協議,這些環境變量就是屬於該協議的內容,因此不論你的server所在的操做系統是Linux仍是Windows,也不論你的server是Apache仍是Nginx,這些變量的名稱和含義都是同樣的。實際就是Apache/Nginx在將這些內容填充到環境變量中,而具體填充規範則來自於CGI接口協議。
在C語言標準中有獲取環境變量值得庫函數——getenv。(頭文件stdlib.h)
//好比
chr* str = NULL;
str = getenv("QUERY_STRING");
對於get請求,能夠從環境變量QUERY_STRING中取出字符串 username=jelly&password=123456。而後程序本身作字符串的解析操做,解析出參數的key和value。而對於post請求,則是直接經過標註輸入(STDIN)來獲取這個參數字符串,好比使用scanf或cin均可以。
在解析了請求、進行了相應的邏輯處理以後(好比檢查用戶名密碼是否一致),CGI程序要向前端頁面返回內容,這是經過標準輸出(STDOUT)完成的,好比printf或cout,你能夠返回xml,json,plain text或一個html網頁等等。這一步完成的是就是HTTP的響應過程。因此在返回直接的數據以前,要先輸出HTTP協議的首部。好比,假設你想返回一個html網頁,那麼你首先要輸出:
cout<<"Content-Type:text/html\n\n"<<endl;
WARNNING:這裏要注意,必定要輸出兩個換行符(\n)。由於HTTP協議的首部和消息實體(如HTML代碼)
之間用空行分割。
後面直接cout出html代碼(好比輸出你剛纔輸入的用戶名成功登錄)。前端頁面就會收到這些html代碼,而後瀏覽器就渲染成網頁啦。這就是一次CGI完成的動態網頁操做了。
Cgicc庫
進行C++的CGI編程,須要手動進行字符串的解析處理,還有自行管理首部。好比資源轉移了,要返回302,而且在首部用Location給出新地址。很顯然,這些東西對於PHP、Python等語言都有內置的解決方案。對於C++就須要第三方庫了。這裏推薦一個GNU的開源庫——
Cgicc。能夠知足經常使用的各種需求,除了解析get/post請求外,還能重定向,還能夠設置Cookie,還能夠上傳文件等等等等。
美中不足的就是Cgicc庫不支持SESSION。可是這個問題不大,咱們能夠很容易使用Cookie來實現SESSION功能。因爲
CGI自己是請求一次就建立一個進程,返回以後進程就結束(下文的FastCGI除外)。這時要在服務端維持一個SESSION的變量可選的解決方案是:用文件存儲或者在Redis、Memcached等內存數據庫中存儲。而發給客戶端的SESSIONID就用Cgicc已經支持的Cookie功能來完成,就能夠了。
CGI的痛點與FastCGI
CGI是一種標準,並不限定語言。因此Java、PHP、Python均可以經過這種方式來生成動態網頁。可是實際上這些動態語言卻不多這樣用。原來是CGI有一大硬傷。那就是每次CGI請求,那麼Apache都有啓動一個進程去執行這個CGI程序,即頗具Unix特點的fork-and-execute。當用戶請求量大的時候,這個fork-and-execute的操做會嚴重拖慢Server的進程。而Java的Servlet技術則是一種常駐內存的技術,不會頻繁的發生進程上下文的建立和銷燬操做。
時勢造英雄,FastCGI技術應運而生。簡單來講,其本質就是一個常駐內存的進程池技術,由調度器負責將傳遞過來的CGI請求發送給處理CGI的handler進程來處理。在一個請求處理完成以後,該處理進程不銷燬,繼續等待下一個請求的到來。FCGI技術一出,CGI又必定程度上煥發了第二春。PHP-FPM自己是使PHP支持FCGI技術的一個Patch,如今已經被歸入PHP標準。固然,支持C++的FCGI技術也出現了,Apache有FCGI的模塊能夠安裝,好比mod_fcgid。
現代CGI的編程範式
前面咱們知道,CGI能夠直接返回一個html網頁。CGI程序自己也能夠進行各類計算、邏輯處理任務。隨着各種web先後端技術的發展,以及大數據、高併發的Server使用場景愈來愈多。現代的CGI的用法,在發生變化。
如今,愈來愈多的任務從後端轉移到前端,前端頁面利用豐富的Js技術來進行更多的處理。
- JS可使用Ajax技術來向後臺CGI發起數據請求。Ajax完成的是不須要刷新整個頁面就能夠加載後端數據(好比從數據庫中取出)。
- CGI通常再也不用於直接返回html頁面,同時將複雜的計算、IO任務下沉到後端(後端能夠進一步進行路由轉發,實現負載均衡)。使CGI做爲先後端之間的中間層。彼時CGI的職能是完成基本的數據交換:解析前端數據請求,再轉發給對應後端;而後從後端取回數據,給前端返回XML或JSON。
- 前端JS利用XML/JSON中的數據來進行填充,繪製出豐富的頁面。