CGI接口原理及實作(2012-12-7 Over)
1.CGI定義:
CGI(CommonGateway Interface)是HTTP伺服器與你的或其它機器上的程式進行“交談”的一種工具,其程式須運作在網絡伺服器上。
2.CGI功能:
絕大多數的CGI程式被用來解釋處理來自表單的輸入資訊,并在伺服器産生相應的處理,或将相應的資訊回報給浏覽器。CGI程式使**網頁具有互動功能。
3.CGI運作環境:
CGI程式在UNIX作業系統上CERN或NCSA格式的伺服器上運作。 在其它作業系統(如:windows NT及windows95等)的伺服器上 也廣泛地使用CGI程式,同時它也适用于各種類型機器。
4.CGI處理步驟:
⑴通過Internet把使用者請求送到伺服器。
⑵伺服器接收使用者請求并交給CGI程式處理。
⑶CGI程式把處理結果傳送給伺服器。
⑷伺服器把結果送回到使用者。
5.CGI伺服器配置:
在許多伺服器cgi-bin是僅能夠放置CGI腳本的目錄。
在Windows平台上将C或C++寫好的程式的Debug或Release版本的.exe程式拷貝到cgi-bin的目錄下(如上圖所示),将.exe改為.cgi也可同樣運作,如下2個圖。

cgi-bin目錄是存放CGI腳本的地方。這些腳本使WWW伺服器和浏覽器能運作外部程式,而無需啟動另一個程式。它是運作在Web伺服器上的一個程式,并由來自于浏覽者的輸入觸發。
CGI程式不是放在伺服器上就能順利運作,如果要想使其在伺服器上順利的運作并準确的處理使用者的請求,則須對所使用的伺服器進行必要的設定。
配置:根據所使用的伺服器類型以及它的設定把CGI程式放在某一特定的目錄中或使其帶有特定的擴充名。
Apache網絡伺服器配置在/var/www/cgi-bin裡(如下圖所示筆者電腦的目錄位置)。C++編譯的可執行檔案可以轉換成擴充名為.cgi的檔案。
更改初始配置的的方法:
<Directory"/var/www/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
<Directory"/var/www/cgi-bin">
Options All
</Directory>
6.CGI接口标準包括标準輸入、環境變量、标準輸出三部分。
介紹 | |
---|---|
1.标準輸入 | CGI程式像其他可執行程式一樣,可通過标準輸入(stdin)從Web伺服器得到輸入資訊,如Form中的資料,這就是所謂的向CGI程式傳遞資料的POST方法。這意味着在作業系統指令行狀态可執行CGI程式,對CGI程式進行調試。POST方法是常用的方法。 |
2.環境變量 | 作系統提供了許多環境變量,它們定義了程式的執行環境,應用程式可以存取它們。Web伺服器和CGI接口又另外設定了自己的一些環境變量,用來向CGI程式傳遞一些重要的參數。CGI的GET方法還通過環境變量QUERY-STRING向CGI程式傳遞Form中的資料。 |
3.标準輸出 | CGI程式通過标準輸出(stdout)将輸出資訊傳送給Web伺服器。傳送給Web伺服器的資訊可以用各種格式,通常是以純文字或者HTML文本的形式,這樣我們就可以在指令行狀态調試CGI程式,并且得到它們的輸出。 |
7.環境變量
環境變量是文本串(名字/值對),可以被OSShell或其他程式設定 ,也可以被其他程式通路。它們是Web伺服器傳遞資料給CGI程式的簡單手段,之是以稱為環境變量是因為它們是全局變量,任何程式都可以存取它們。
下面是CGI程式設計中常常要用到的一些環境變量。
環境變量 | 意義 |
---|---|
SERVER_NAME | CGI腳本運作時的主機名和IP位址. |
SERVER_SOFTWARE | 你的伺服器的類型如: CERN/3.0 或 NCSA/1.3. |
GATEWAY_INTERFACE | 運作的CGI版本. 對于UNIX伺服器, 這是CGI/1.1. |
SERVER_PROTOCOL | 伺服器運作的HTTP協定. 這裡當是HTTP/1.0. |
SERVER_PORT | 伺服器運作的TCP口,通常Web伺服器是80. |
REQUEST_METHOD | POST 或 GET, 取決于你的表單是怎樣遞交的. |
HTTP_ACCEPT | 浏覽器能直接接收的Content-types, 可以有HTTP Accept header定義. |
HTTP_USER_AGENT | 遞交表單的浏覽器的名稱、版本 和其他平台性的附加資訊。 |
HTTP_REFERER | 遞交表單的文本的 URL,不是所有的浏覽器都發出這個資訊,不要依賴它 |
PATH_INFO | 附加的路徑資訊, 由浏覽器通過GET方法發出. |
PATH_TRANSLATED | 在PATH_INFO中系統規定的路徑資訊. |
SCRIPT_NAME | 指向這個CGI腳本的路徑, 是在URL中顯示的(如, /cgi-bin/thescript). |
QUERY_STRING | 腳本參數或者表單輸入項(如果是用GET遞交). QUERY_STRING包含URL中問号後面的參數. |
REMOTE_HOST | 遞交腳本的主機名,這個值不能被設定. |
REMOTE_ADDR | 遞交腳本的主機IP位址. |
REMOTE_USER | 遞交腳本的使用者名. 如果伺服器的authentication被激活,這個值可以設定。 |
REMOTE_IDENT | 如果Web伺服器是在ident (一種确認使用者連接配接你的協定)運作, 遞交表單的系統也在運作ident, 這個變量就含有ident傳回值. |
CONTENT_TYPE | 如果表單是用POST遞交, 這個值将application/x-www-form-urlencoded. 在上載檔案的表單中, content-type 是個 multipart/form-data. |
CONTENT_LENGTH | 對于用POST遞交的表單,标準輸入口的位元組數. |
REQUEST-METHOD:指的是當Web伺服器傳遞資料給CGI程式時所采用的方法,分為GET和POST兩種方法。
【GET和POST方法的差別】:GET方法僅通過環境變量(如QUERY-STRING)傳遞資料給CGI程式,而POST方法通過環境變量和标準輸入傳遞資料給CGI程式,是以POST方法可較友善地傳遞較多的資料給CGI程式。
問題 | ||
---|---|---|
GET方法 | 通過在URL中嵌入的形式傳遞參數。對CGI程式而言,在GET method中傳遞的參數要通過化境變量“QUERY-STRING”來接收。 | 1) 參數的内容作為URL資訊,使用者可以看到;2) 有大小的限制 |
POST方法 | CGI程式從标準輸入接收參數。與GET方法不同的是,參數的内容從URL資訊中不能獲得,對于大小也沒有限制。 | 與GET方法問題1),2)完全相反。 |
CONTENT-LENGTH:傳遞給CGI程式的資料字元數(位元組數)。
在C語言程式中,要訪向環境變量,可使用getenv()庫函數。例如:
if (getenv (″CONTENT-LENGTH″))n=atoi(getenv(″CONTENT-LENGTH″));
請注意程式中最好調用兩次getenv():第一次檢查是否存在該環境變量,第二次再使用該環境變量。這是因為函數getenv()在給定的環境變量名不存在時,傳回一個NULL(空)指針,如果你不首先檢查而直接引用它,當該環境變量不存在時會引起CGI程式崩潰。
8. CGI的工作原理
CGI是一個WEB伺服器提供資訊服務的标準接口,通過這樣一個接口,WEB伺服器能夠執行程式,并将程式輸出的資訊傳回給浏覽器。因為在WEB網上的資料都是靜态的,通過CGI程式能夠動态的處理浏覽者的請求,如儲存使用者輸入的資訊,根據使用者資訊傳回相關的資料等等。當用戶端發送一個CGI請求給WEB伺服器後,WEB伺服器将根據CGI程式的類型決定資料向CGI程式的傳送方式,一般來講是通過标準輸入/輸出流和環境變量來與CGI程式間傳遞資料。
CGI輸入輸出原理
CGI的輸入/輸出方法:CGI程式通過标準輸入(STDIN)和标準輸出(STDOUT)來進行輸入輸出,STDIN和STDOUT是兩個預先定義好的檔案指針。你可以利用檔案讀寫函數來對其進行操縱。
此外CGI程式還通過環境變量來得到輸入,隻不過環境變量中提供的是一些常用的資訊,并且通常不包括使用者在WEB頁面中輸入的資訊(除使用下面講的GET方法時,通過檢查環境變量QUERY_STRING來得到輸入資料),而STDIN通常用來傳遞使用者輸入的資訊。
在輸入時所使用的POST/GET方法:在WEB頁面向CGI發送資料時通常采用兩種方法:GET/POST,GET方法将資料附加在URL後發送,如:/cgi/a_cgi_test.exe?your_data,CGI程式通過檢查環境變量QUERY_STRING來得到輸入資料。
示例一、下圖即是GET方法!
上圖的對應程式為:
//2012-12-5 GET c程式示例..
void main(void)
{// 本程式将使用者輸入的資料列印出來
fprintf(stdout,"content-type:text/plain\n\n");
// 輸出一個CGI标題,這行代碼的意義後面會講解
char *pszMethod;
pszMethod =getenv("REQUEST_METHOD");
if(strcmp(pszMethod,"GET") == 0)
{ //GET method
//讀取環境變量來擷取資料
printf("This is GETMETHOD!\n");
printf("SERVER_NAME:%s\n",getenv("SERVER_NAME"));
printf("REMOTE_ADDR:%s\n",getenv("REMOTE_ADDR"));
fprintf(stdout,"input data is:%s\n",getenv("QUERY_STRING"));
}
else
{ // POST method
//讀取STDIN來擷取資料
intiLength=atoi(getenv("CONTENT_LENGTH"));
printf("This is POSTMETHOD!\n");
fprintf(stdout,"input data is:\n");
for(int i=0;i<iLength;i++)
{
char cGet=fgetc(stdin);
fputc(cGet,stdout);
}
}
}
示例二、下圖即是POST程式示例:
void unencode(char *src, char *last, char *dest)
{
// str = hello+there%21 此處跳過data=...
// last = ; 已到末尾.
// dest= ; 空串.
//解碼原則
//原則1: '+'變' ';
//原則2: '%xx'變成對應的16進制ASCII碼值;
for(; src != last; src++, dest++)
{
if(*src == '+')
{
*dest = ' ';
}
else if(*src == '%')
{
int code;
if(sscanf(src+1, "%2x", &code) != 1)
{
code = '?';
}
*dest = code;
src +=2;
}
else
{
*dest = *src;
}
}
*dest = '\n';
*++dest = '\0';
}
intmain(void)
{
char *lenstr;
char input[MAXINPUT], data[MAXINPUT];
long len;
printf("%s%c%c\n","Content-Type:text/html;charset=iso-8859-1",13,10);
printf("<TITLE>Response</TITLE>\n");
lenstr =getenv("CONTENT_LENGTH");
printf("CONTENT_LENGTH =%s\n",lenstr);
if(lenstr == NULL ||sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)
{
printf("<P>Error ininvocation - wrong FORM probably.");
}
else
{
FILE *f;
fgets(input, len+1, stdin); //add by ycy從輸入流中擷取字元串.
unencode(input+EXTRA, input+len,data);
f = fopen(DATAFILE,"a");
if(f == NULL)
{
printf("<P>Sorry,cannot store your data.");
}
else
{
fputs(data, f); //add byycy 将資料存儲在對對應的檔案中.
}
fclose(f);
printf("<P>Thank you!Your contribution has been stored.");
}
return 0;
}
請求過程即是:
(1)send發送按鈕--->(2)調用post.cgi--->(3)将資料存儲在datadata.txt裡面。
示例三、下圖即是GET/POST程式示例。
綜合執行個體:在上面兩個程式上的擴充(應用Get方法及QUERY_STRING),
如上兩圖所示,在表單(cgi接口的表單隻是在html語言的基礎上用C或C++實作的擴充操作而已)的基礎上,送出按鈕對應的另一個CGI接口(6.exe或6.cgi),這樣通過Get方法及QUERY_STRING參量就可以完成輸出操作。
而POST方法則會将資料送入CGI程式的STDIN輸入流。在表單(FORM)中的各個變量都會成為name=value的形式向WEB伺服器發送,多個資料間用&分隔,如:name=value&name2=value2。其中名字(name,name2)是Form中定義的INPUT、SELECT或TEXTAREA等标置(Tag)名字,值是使用者輸入或選擇的标置值。
如上面說講,在CGI程式輸出時必須先輸出一個CGI标題,标題共有以下三類:
- Location: 标題,指明輸出另一個文檔的URL,例如 fprintf(stdout,"Location: http://www.vchelp.net/ nn");
- Content-Type: 标題,指明發送的資料的MIME類型,例如 fprintf(stdout,"Content-Type:text/htmlnn");
- Status: 标題,指明HTTP狀态碼,例如 fprintf(stdout,"Status:200nn");
注意每種标題後都必須跟一個換行和一個空行。
MIME類型以類型/子類型的形式來表示,下面是一些常用的類型/子類型的組合:
· Text/plain 普通文本類型
· Text/html HTML格式的文本類型
· Audio/basic 八位聲音檔案格式,字尾為.au
· Video/mpeg MPEG檔案格式
· Video/quicktime QuickTime檔案格式
· Image/gif GIF圖形檔案
· Image/jpeg JPEG圖形檔案
· Image/x-xbitmap X bitmap圖形檔案,字尾為.xbm
有了上面的知識我們就可以寫出一些CGI程式,首先需要對輸入資料進行分析,方法為:每當找到字元=,标志着一個Form變量名字的結束;每當找到字元& ,标志着一個Form變量值的結束。請注意輸入資料的最後一個變量的值不以&結束。這樣我們可以将輸入資料分解為一組一組的值。
但随後會發現CGI的輸入并不規則,例如有時會出現類似下面格式的輸入字元号串:
filename=hello&cmd=world+I%27
,這是因為浏覽器對一些上傳的特殊字元進行了編碼,是以在将資料分解開後需要進行解碼,
解碼規則為:
1)+: 将+轉換成空格符;
2) %xx: 用其十六進制ASCII碼值表示的特殊字元(%作為為轉意符)。根據值xx将其轉換成相應的ASCII字元。對Form變量名和變量值都要進行這種轉換。
作者:銘毅天下
來源:CSDN
原文:
https://blog.csdn.net/laoyang360/article/details/8288619版權聲明:本文為部落客原創文章,轉載請附上博文連結!