CGIC简明教程
http://deepfuture.iteye.com/blog/1435339
CGIC簡(jiǎn)明教程1:使用CGIC的基本思路
C語(yǔ)言編程是一項(xiàng)復(fù)雜且容易出錯(cuò)的工作,所以在完成復(fù)雜任務(wù)時(shí),一定要選擇合適的庫(kù)。對(duì)于用C語(yǔ)言編寫(xiě)CGI程序則更是如此。CGIC是非常優(yōu)秀的C語(yǔ)言CGI庫(kù)函數(shù)。 其下載地址為:www.boutell.com/cgic/#obtain,現(xiàn)在的版本號(hào)是2.05。本站從今天開(kāi)始,將逐步介紹如何使用CGIC完成各種操作,也可以說(shuō)是一個(gè)Tutorial。(注:本系列涉及的編程環(huán)境都是Linux,Windows用戶需要對(duì)用到的操作系統(tǒng)命令稍作修改)
本文綱要 :CGIC的安裝、測(cè)試安裝、使用CGIC的基本思路;
1) CGIC的下載安裝
從上面提供的官方網(wǎng)址下載了CGIC庫(kù)之后,解開(kāi)壓縮包,里面有大約10個(gè)文件,有用的是:
cgic.h:頭文件;
cgic.c:CGIC的源代碼文件;
cgictest.c:CGIC庫(kù)的作者提供的一個(gè)CGI程序例子;
capture.c:用于調(diào)試CGI程序的工具;
Makefile:安裝CGIC的腳本文件;
可以看到,整個(gè)庫(kù)實(shí)際上就是cgic.c一個(gè)文件,可以說(shuō)是非常的精煉。我們可以把CGIC安裝為操作系統(tǒng)的一個(gè)動(dòng)態(tài)鏈接庫(kù),這樣我們每次編譯的時(shí)候,就不需要有cgic.c這個(gè)源文件了。但是由于需要(以后將會(huì)看到),我們將修改cgic.c代碼,所以我們不把它安裝進(jìn)系統(tǒng)。每次編譯的時(shí)候,只要把cgic.c和cgic.h放到當(dāng)前文件夾就好了。
2) 測(cè)試安裝
在開(kāi)始編寫(xiě)你自己的CGI程序之前,一定要先走通他的例子程序,免得后來(lái)程序出錯(cuò)的時(shí)候還不知道是配置有問(wèn)題,還是你的程序代碼有問(wèn)題。我們用他自帶cgictest.c來(lái)實(shí)現(xiàn)自己的第一個(gè)C語(yǔ)言CGI程序。
你可以新建一個(gè)工作目錄,用于存放你的CGI程序源代碼,把cgic.h, cgic.c, cgictest.c三個(gè)文件拷貝到這個(gè)目錄,然后建立一個(gè)Makefile文件,其內(nèi)容為:
????? test.cgi:cgictest.c cgic.h cgic.c
????????? gcc -wall cgictest.c cgic.c -o test.cgi?
需要提醒的是,第二行開(kāi)頭一定是一個(gè)tab鍵(且僅有一個(gè)),不能使用空格。保存好Makefile的內(nèi)容之后,執(zhí)行make命令:make
我們看到,當(dāng)前目錄下應(yīng)該多了一個(gè)test.cgi文件。
在你的網(wǎng)站根目錄下建立一個(gè)cgi-bin目錄(當(dāng)然名字可以任意取,但作為習(xí)慣,一般叫做cgi-bin),然后在Apache的配置文件里賦予其執(zhí)行CGI代碼的權(quán)限,權(quán)限修改完之后要重啟Apache。完成之后,把剛才生成的test.cgi放到cgi-bin目錄中。此時(shí)我們可以在瀏覽器中輸入以下地址進(jìn)行訪問(wèn):
http://127.0.0.1/cgi-bin/test.cgi
如果正常的話,應(yīng)該看到一個(gè)網(wǎng)頁(yè)被展示出來(lái)。這樣,第一個(gè)C語(yǔ)言的CGI程序就運(yùn)行起來(lái)了。如果瀏覽器報(bào)錯(cuò),那么多半是配置Apache的時(shí)候有些操作沒(méi)有正確完成。
3) 使用CGIC的基本思路
從cgic.c的代碼可以看出,它定義了main函數(shù),而在cgictest.c中定義了一個(gè)cgiMain函數(shù)。也就是說(shuō),對(duì)于使用CGIC編寫(xiě)的CGI程序,都是從cgic.c中的代碼進(jìn)入,在庫(kù)函數(shù)完成了一系列必要的操作(比如解析參數(shù)、獲取系統(tǒng)環(huán)境變量)之后,它才會(huì)調(diào)用你的代碼(從你定義的cgiMain進(jìn)入)。
另外一點(diǎn)就是,cgi程序輸出HTML頁(yè)面的方式都是使用printf把頁(yè)面一行一行地打印出來(lái),比如cgictest.c中的這一段代碼:
fprintf(cgiOut, "<textarea NAME=\"address\" ROWS=4 COLS=40>\n");
fprintf(cgiOut, "Default contents go here. \n");
fprintf(cgiOut, "</textarea>\n");
上面這段代碼的運(yùn)行結(jié)果就是在頁(yè)面上輸出一個(gè)textarea。 第一個(gè)參數(shù)cgiOut實(shí)際上就是stdin,所以我們可以直接使用printf,而不必使用fprintf。不過(guò)在調(diào)試的時(shí)候會(huì)用到fprintf來(lái)重定向輸出。
這種方式與Java Servlet非常類(lèi)似,Servlet也是通過(guò)調(diào)用打印語(yǔ)句System.out.println(…)來(lái)輸出一個(gè)頁(yè)面。(不過(guò)后來(lái)Java推出了JSP來(lái)克服這種不便。)但是與Servlet不同的地方在于,使用C語(yǔ)言的我們還要自己輸出HTML頭部(聲明文檔類(lèi)型):
cgiHeaderContentType("text/html");
這個(gè)語(yǔ)句的調(diào)用一定要在所有printf語(yǔ)句之前。而這個(gè)語(yǔ)句執(zhí)行的任務(wù)實(shí)際上就是:
void cgiHeaderContentType(char *mimeType) {
?? fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
}
這個(gè)語(yǔ)句告訴瀏覽器,這次傳來(lái)的數(shù)據(jù)是什么類(lèi)型,是一個(gè)HTML文檔,還是一個(gè)bin文件… 如果是個(gè)HTML文檔,就通過(guò)瀏覽器窗口顯示,如果是一個(gè)bin(二進(jìn)制)文件,則打開(kāi)下載窗口,讓用戶選擇是否保存文件以及保存文件的路徑。理解了這幾點(diǎn)之后,你就可以編寫(xiě)自己的CGIC程序了。新建一個(gè)文件test.c試試:
#include <stdio.h>?
#include "cgic.h"?
#include <string.h>?
#include <stdlib.h>?
int cgiMain() {?
cgiHeaderContentType("text/html");?
fprintf(cgiOut, "<HTML><HEAD>\n");?
fprintf(cgiOut, "<TITLE>My First CGI</TITLE></HEAD>\n");?
fprintf(cgiOut, "<BODY><H1>Hello CGIC</H1></BODY>\n");?
fprintf(cgiOut, "</HTML>\n");?
return 0;?
}?
把Makefile文件中的cgitest.c全部換稱(chēng)test.c,保存,再執(zhí)行make命令即可。此時(shí)通過(guò)瀏覽器訪問(wèn),會(huì)在頁(yè)面上看到一個(gè)大大的“Hello CGIC”。
CGIC簡(jiǎn)明教程2:獲取Get請(qǐng)求字符串
Get請(qǐng)求就是我們?cè)跒g覽器地址欄輸入U(xiǎn)RL時(shí)發(fā)送請(qǐng)求的方式,或者我們?cè)贖TML中定義一個(gè)表單(form)時(shí),把a(bǔ)ction屬性設(shè)為“Get”時(shí)的工作方式;Get請(qǐng)求字符串就是跟在URL后面以問(wèn)號(hào)“?”開(kāi)始的字符串,但不包括問(wèn)號(hào)。比如這樣的一個(gè)請(qǐng)求:
http://127.0.0.1/cgi-bin/out.cgi?ThisIsTheGetString
在上面這個(gè)URL中,“ThisIsTheGetString”就是Get請(qǐng)求字符串。在進(jìn)入我們自己編寫(xiě)的cgi代碼之前,CGIC庫(kù)已經(jīng)事先把這個(gè)字符串取到了,我們可以在程序中直接獲得,要做的僅僅是在你編寫(xiě)的cgiMain方法前面加入以下聲明:
extern char *cgiQueryString;
現(xiàn)在給出一個(gè)簡(jiǎn)單的例子,這個(gè)例子跟上一篇的測(cè)試程序非常相似,只不過(guò)程序的輸出是使用者輸入的Get請(qǐng)求字符串。
#include <stdio.h>
#include "cgic.h"?
#include <string.h>?
#include <stdlib.h>?
extern char *cgiQueryString;?
int cgiMain() {?
cgiHeaderContentType("text/html");?
fprintf(cgiOut, "<HTML><HEAD>\n");?
fprintf(cgiOut, "<TITLE>My CGIC</TITLE></HEAD>\n");?
fprintf(cgiOut, "<BODY>");?
fprintf(cgiOut, "<H1>%s</H1>",cgiQueryString);?
fprintf(cgiOut, "</BODY>\n");?
fprintf(cgiOut, "</HTML>\n");?
return 0;?
}?
假設(shè)把這個(gè)程序編譯成out.cgi(編譯方法參見(jiàn)上一篇),并部署到Web服務(wù)器的cgi-bin目錄下,當(dāng)用戶在瀏覽器地址欄輸入本文開(kāi)頭給出的URL字符串時(shí),瀏覽器頁(yè)面上會(huì)顯示:ThisIsTheGetString
我們也可以編寫(xiě)一個(gè)用于測(cè)試的HTML頁(yè)面:
<html>?
<head>?
<title>Test</title>?
</head>?
<body>?
<form action="cgi-bin/out.cgi" method="get">?
<input type="text" name="theText">?
<input type="submit" value="Continue →">?
</form>?
</body>?
</html>?
文件的部署結(jié)構(gòu)應(yīng)該為:
|test.html
|---cgi-bin/out.cgi
大家可以試試,通過(guò)瀏覽器訪問(wèn)http://127.0.0.1/test.html,在文本框內(nèi)輸入一些字符,并點(diǎn)擊提交按鈕,然后就可以看到cgi程序的執(zhí)行結(jié)果:把在文本框輸入的字符原樣顯示在瀏覽器上。
CGIC簡(jiǎn)明教程3:反轉(zhuǎn)義
瀏覽器在發(fā)送Get請(qǐng)求時(shí),會(huì)把請(qǐng)求字符串進(jìn)行轉(zhuǎn)義操作(英文術(shù)語(yǔ)為: escape); 比如,我們?cè)诘刂窓谳斎?#xff08;注意最后”it’s me”中的空格):http://localhost/~Jack/cgi-bin/out.cgi?it's me
瀏覽器會(huì)把它轉(zhuǎn)義為:http://localhost/~Jack/cgi-bin/out.cgi?it's%20me
在上一篇最后給出的例子中,如果在文本框內(nèi)輸入 it's me
你會(huì)發(fā)現(xiàn),瀏覽器最終發(fā)送的請(qǐng)求為?http://localhost/~Jack/cgi-bin/out.cgi?theText=it%27s+me
通過(guò)CGIC,我們可以把這些被轉(zhuǎn)義后的字符還原為我們本來(lái)的輸入,這個(gè)過(guò)程就叫“反轉(zhuǎn)義” (Unescape)。不過(guò)這個(gè)過(guò)程有點(diǎn)像hack他的代碼。
整個(gè)過(guò)程分三個(gè)步驟:
1)打開(kāi)cgic.c,找到這一行語(yǔ)句:
static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
注意,我們要找的只是這個(gè)函數(shù)聲明,不是函數(shù)定義;
2)在這個(gè)函數(shù)聲明語(yǔ)句的上方,你會(huì)看到一個(gè)結(jié)構(gòu)體定義:
typedef enum {?
cgiUnescapeSuccess,?
cgiUnescapeMemory?
} cgiUnescapeResultType;?
把這幾行語(yǔ)句復(fù)制到cgic.h文件中,并在這里把它注釋掉;同時(shí)還要?jiǎng)h除在第一步中找到的函數(shù)聲明語(yǔ)句中的“static”關(guān)鍵字。
3)我們現(xiàn)在就可以使用反轉(zhuǎn)義函數(shù)cgiUnescapeChars了:在你自己的代碼(按照慣例,還是test.c)中,加入以下聲明語(yǔ)句即可
extern cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
接下來(lái)我們給出一段完整的test.c代碼?
#include <stdio.h>
#include "cgic.h"?
#include <string.h>?
#include <stdlib.h>?
extern char *cgiQueryString;?
extern cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);?
int cgiMain() {?
char * buffer;?
cgiHeaderContentType("text/html");?
fprintf(cgiOut, "<HTML><HEAD>\n");?
fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n");?
fprintf(cgiOut, "<BODY>");?
cgiUnescapeChars(&buffer, cgiQueryString, strlen(cgiQueryString));?
fprintf(cgiOut, "<H1>%s</H1>",buffer);?
fprintf(cgiOut, "</BODY>\n");?
fprintf(cgiOut, "</HTML>\n");?
free(buffer);?
return 0;?
}?
值得注意的是,buffer的存儲(chǔ)空間是cgiUnescapeChars幫你分配的,但最后要由你自己來(lái)釋放(free),這一點(diǎn)千萬(wàn)不可忘記。下面你可以結(jié)合上一篇給出的測(cè)試用html代碼試試該cgi程序的運(yùn)行結(jié)果,也可以直接在瀏覽器地址欄輸入一些帶有特殊符號(hào)的字符串。
最后講一下為什么不得不用這種hacker的方式來(lái)完成該任務(wù),而CGIC不顯式提供?CGIC的出發(fā)點(diǎn)是,我們平時(shí)只需要解析請(qǐng)求中的鍵值對(duì),比如:”?q=nice&client=IE”,當(dāng)我們?cè)诜?wù)端查詢“q”的值時(shí),我們就能得到“nice”。CGIC有一族函數(shù)幫助我們完成這個(gè)任務(wù),比如cgiFormString(以后會(huì)講到)。在解析這種請(qǐng)求格式的時(shí)候,如果我們提供的參數(shù)值含有被轉(zhuǎn)義的字符,那么CGIC就會(huì)在內(nèi)部調(diào)用cgiUnescapeChars完成反轉(zhuǎn)義。但是,有時(shí)候我們會(huì)發(fā)送非常復(fù)雜的Get請(qǐng)求字符串,但并不是“鍵-值”對(duì)的格式。這就需要直接使用cgiUnescapeChars進(jìn)行反轉(zhuǎn)義了。例如:假設(shè)我們有個(gè)服務(wù)端cgi程序chat.cgi,這是個(gè)網(wǎng)絡(luò)聊天機(jī)器人(也許你可以開(kāi)發(fā)自己的Web版MSN機(jī)器人、QQ機(jī)器人)。如果我們發(fā)送如下請(qǐng)求:http://127.0.0.1/cgi-bin/chat.cgi?"this?is a cgi user"
那么chat.cgi就會(huì)把“this is a cgi user”當(dāng)做你對(duì)它說(shuō)的話,經(jīng)過(guò)處理,它會(huì)回復(fù)一段語(yǔ)句。為了方便,我們并沒(méi)有寫(xiě)成“鍵-值”對(duì)的形式。這個(gè)時(shí)候被我們hack的cgiUnescapeChars就能派上用場(chǎng)了。
CGIC簡(jiǎn)明教程4:獲取請(qǐng)求中的參數(shù)值
在提交一個(gè)表單(form)時(shí),怎樣把表單內(nèi)的值提取出來(lái)呢?比如下面這個(gè)表單:
<form action="cgi-bin/out.cgi" method="POST">
??? <input type="text" name="name" />
??? <input type="text" name="number" />
??? <input type="submit" value="Submit" />
</form>
當(dāng)out.cgi收到請(qǐng)求時(shí),需要把輸入框”name”和輸入框”number”內(nèi)的值提取出來(lái)。而且不管form中的action是GET還是POST,都要有效。下面給出示例代碼:
#include <stdio.h>
#include "cgic.h"?
#include <string.h>?
#include <stdlib.h>???
int cgiMain() {?
??? char name[241];?
??? char number[241];?
??? cgiHeaderContentType("text/html");?
??? fprintf(cgiOut, "<HTML><HEAD>\n");?
??? fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n");?
??? fprintf(cgiOut, "<BODY>");?
??? cgiFormString("name", name, 241);?
??? cgiFormString("number", number, 241);?
??? fprintf(cgiOut, "<H1>%s</H1>",name);?
??? fprintf(cgiOut, "<H1>%s</H1>",number);?
??? fprintf(cgiOut, "</BODY>\n");?
??? fprintf(cgiOut, "</HTML>\n");?
??? return 0;?
}
從上面的代碼可以看出,第13行和第14行獲取了輸入框的值。獲取輸入?yún)?shù)值在CGIC中其實(shí)有一族函數(shù),cgiFormString是其中最常用的一個(gè)。cgiFormStringNoNewlines用來(lái)去掉換行符(如果用戶是在一個(gè)TextArea里輸入字符的話);cgiFormStringSpaceNeeded用于測(cè)試輸入值的長(zhǎng)度,可以以此為依據(jù),然后按需精確分配緩沖區(qū)
總結(jié)
- 上一篇: linux 路由 route命令
- 下一篇: cgic: 为C语言编写CGI的C函数库