程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++後台實踐:古老的CGI與Web開發

C++後台實踐:古老的CGI與Web開發

編輯:關於C++

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

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

談到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。

在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請求數據 這裡知講第一種(最簡單的):

表單提交

<form action="/cgi-bin/hello.cgi" method="get">
	<table>
	<tbody>
		<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>
	</tbody>
	</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/post

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"<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中的數據來進行填充,繪制出豐富的頁面。  
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved