1簡介
.CGI:通用網關接口(Common Gateway Interface)是一個Web服務器主機提供信息服務的標准接口,服務器和客戶端之間的通信,是客戶端的瀏覽器和服務器端的http服務器之間的HTTP通信。通過CGI接口,Web服務器就能夠獲取客戶端提交的信息,轉交給服務器端的CGI程序進行處理,最后返回結果給客戶端。
2.組成
組成Cgi程序的是兩部分,一部分是html頁面,就是用戶看到的東西。另一部分則是運行在務器上的程序。一般來說,我們先寫html頁面,再寫程序
3.原理
一 什么是CGI
CGI(The Common Gateway Interface):通用網關接口,定義web服務器和客戶腳本進行信息交互的一系列標准。
二 web瀏覽器
為了了解CGI的概念,讓我們來看看當我們單擊一個超鏈接來瀏覽一個特定的web頁或URL的時候,背后會發生什么事?
(1)瀏覽器首先會鏈接HTTP web 服務器並且請求一個URL 頁面;
(2) WEB服務器將會解析這個URL並且查詢請求的文件名,如果找到了請求文件服務器就會將這個文件發送回瀏覽器,否則發送回一個包含錯誤信息提示的頁面指示你請求的是一個服務器並不包含的文件。
(3)WEB瀏覽器將接受來自服務器端的響應,並且向發出請求的用戶顯示接收到的文件。
然而,HTTP服務器也可能會以如何這種方式進行配置,那就是無論什么時候接受到對特定目錄下的文件的請求,服務器不會將這個文件發送回客戶端,而是它作為一個程序被服務器執行,並產生出輸出發送回客戶端瀏覽器進行顯示。
CGI(The Common Gateway Interface)是一個標准化的協議,能夠使應用程序(通常稱為CGI程序或CGI腳本)同web服務器和客戶端進行交互。CGI程序能夠用 Python, PERL, Shell, C or C++等語言來實現。
三 CGI程序結構圖
下圖簡單的展示了CGI程序架構
四 web服務器配置
在你着手寫CGI程序之前,確保你的web服務器支持CGI程序並且配置成處理CGI程序。所有的能夠被HTTP服務器執行的CGI程序都被存放在預先配 置好的目錄下面,這個目錄叫做CGI目錄,並且按照約定命名為 /var/www/cgi-bin,並且約定CGI文件的后綴名為.cgi ,盡管它們是c++可執行文件。
一般的,Apache 服務器在/var/www/cgi-bin目錄下配置文件來運行CGI程序,如果你想要聲明另外的目錄來運行CGI腳本,你需要修改httpd.conf 文件中的部分內容:
<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all </Directory> <Directory "/var/www/cgi-bin"> Options All </Directory>
五 第一個CGI腳本
以下是一段簡短的CGI腳步代碼
#include <iostream> using namespace std; int main () { cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Hello World - First CGI Program</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>Hello World! This is my first CGI program</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
編譯上述代碼並且將二進制可執行文件命名為cplusplus.cgi,保存路徑為/var/www/cgi-bin目錄下,運行chmod 755 cplusplus.cgi 命令使得該文件為可執行的。現在,如果你點擊cplusplus.cgi然后就會產生如下輸出:
Hello World! This is my first CGI program
上面的C++程序是一個將輸出寫入標准輸出文件(stdout)的簡單程序。這段代碼中有一個很重要的一點那就是第一行代碼:Content- type:text/html\r\n\r\n,這行被發送回瀏覽器,指明瀏覽器顯示的文本類型。現在你應該了解了CGI的基本概念了,你也可以使用 python寫出更多復雜的CGI程序,C++ CGI程序能與其他任何外部系統進行信息交互,例如像RDBMS。
六 HTTP報文頭部
這行字符串” Content-type:text/html\r\n\r\n”是發送回瀏覽器的HTTP報文頭部的一部分,所有的HTTP報文頭部都有如下格式:
HTTP Field Name: Field Content For Example Content-type: text/html\r\n\r\n
下表中包含其他一些重要的HTTP報文信息,這些信息在CGI編程中經常會用到。
Header | Description |
---|---|
Content-type: | A MIME string defining the format of the file being returned. Example is Content-type:text/html |
Expires: Date | The date the information becomes invalid. This should be used by the browser to decide when a page needs to be refreshed. A valid date string should be in the format 01 Jan 1998 12:00:00 GMT. |
Location: URL | The URL that should be returned instead of the URL requested. You can use this filed to redirect a request to any file. |
Last-modified: Date | The date of last modification of the resource. |
Content-length: N | The length, in bytes, of the data being returned. The browser uses this value to report the estimated download time for a file. |
Set-Cookie: String | Set the cookie passed through the string |
七 CGI環境變量
所有的CGI程序將會使用到下列的CGI環境變量,這些變量在CGI程序中起着重要的作用。
Variable Name | Description |
---|---|
CONTENT_TYPE | The data type of the content. Used when the client is sending attached content to the server. For example file upload etc. |
CONTENT_LENGTH | The length of the query information. It's available only for POST requests |
HTTP_COOKIE | Return the set cookies in the form of key & value pair. |
HTTP_USER_AGENT | The User-Agent request-header field contains information about the user agent originating the request. Its name of the web browser. |
PATH_INFO | The path for the CGI script. |
QUERY_STRING | The URL-encoded information that is sent with GET method request. |
REMOTE_ADDR | The IP address of the remote host making the request. This can be useful for logging or for authentication purpose. |
REMOTE_HOST | The fully qualified name of the host making the request. If this information is not available then REMOTE_ADDR can be used to get IR address. |
REQUEST_METHOD | The method used to make the request. The most common methods are GET and POST. |
SCRIPT_FILENAME | The full path to the CGI script. |
SCRIPT_NAME | The name of the CGI script. |
SERVER_NAME | The server's hostname or IP Address |
SERVER_SOFTWARE | The name and version of the software the server is running. |
下面這段代碼列出了所有的CGI變量,點擊Get Environment可看結果。
#include <iostream> using namespace std; const string ENV[ 24 ] = { "COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT", "HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION", "HTTP_HOST", "HTTP_USER_AGENT", "PATH", "QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_NAME","SERVER_PORT","SERVER_PROTOCOL", "SERVER_SIGNATURE","SERVER_SOFTWARE" }; int main () { cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>CGI Envrionment Variables</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<table border = \"0\" cellspacing = \"2\">"; for ( int i = 0; i < 24; i++ ) { cout << "<tr><td>" << ENV[ i ] << "</td><td>"; // attempt to retrieve value of environment variable char *value = getenv( ENV[ i ].c_str() ); if ( value != 0 ){ cout << value; }else{ cout << "Environment variable does not exist."; } cout << "</td></tr>\n"; } cout << "</table><\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
八 C++CGI庫
在該FTP服務器上ftp://ftp.gnu.org/gnu/cgicc/ 提供了C++ CGI庫以供下載,我們從上面下載CGI 庫並一下步驟進行安裝:
$tar xzf cgicc-X.X.X.tar.gz
$cd cgicc-X.X.X/
$./configure --prefix=/usr
$make
$make install
並且你可以閱讀相關文檔。C++ CGI Lib Documentation 。
九 GET 與POST方法
當你需要從瀏覽器客戶端傳遞信息至web服務器端並最終送至CGI程序的時候,你將必然會遇到很多的問題。大部分的瀏覽器使用兩種方法發送信息至瀏覽器:GET方法和POST方法,進行過WEB開發的人應該對其很熟悉。
1. 使用GET方法發送信息
GET方法將編碼過的用戶信息附加在頁面請求上發送,頁面請求和這些編碼信息使用?進行分割,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET方法是瀏覽器發送信息之服務器端所采用的默認的方法,采用這種方法發送時,在你的瀏覽器地址欄上在URL后面會附加上一串字符串,如果你傳輸密碼或 其他敏感信息至服務器端的時候不要使用GET方法,GET方法有長度限制,在一個請求字符串中,最多只能發送1024的字符。
當使用GET方法的時候,HTTP報文頭采用QUERY_STRING發送信息,並且將通過QUERY_STRING環境變量進入你的CGI程序。
您能夠使用簡單的鍵-值組合附加在URL后傳遞信息,或者你也可使用HTML中的<FORM>標簽通過使用GET方法來傳遞信息。
2. 使用POST方法發送信息
CGI程序中較為通用的且更為可靠地傳遞信息的方法是POST方法,POST傳遞的報文信息和GET方法沒什么兩樣,但是跟GET方法的將字符串信息附加 於URL之后並且用?分隔有所區別的是,POST方法使用分離的報文段分別發送URL和要傳輸的信息。這些信息會被CGI腳本以標准輸入的形式接收。
(注:在原文中作者給出了HTML各種控件傳遞信息給CGI腳本的例子,有興趣的朋友可以去看看)
十 在CGI中使用Cookies
服務器可能會以Cookies的形式發送數據給客戶端瀏覽器上,瀏覽器也許會接收這些Cookies,並且會以簡單文本的形式存儲在用戶的硬盤上,當用戶 訪問該web站點的另外頁面的時候,這些Cookies就會有用處了,服務器就會據此知道用戶記錄了那些信息。
Cookies信息格式包含如下5個變量:
(1) Expires:包含Cookies的過期信息。如果變量值為空,當客戶端關閉瀏覽器時,Cookies就會過期。
(2) Domain:web站點的域名信息。
(3) Path:設置Cookies的web頁或目錄的路徑。如果想要從任何頁面或目錄獲取Cookies信息,此變量設為空值。
(4) Secure:如果該字段設置為"secure",那么Cookies將只能被安全服務器獲取,如果該字段為空,則沒有該限制。
(5) Name=Value:Cookies以鍵-值對的形式設置或獲取。
1. 設置Cookies
發送Cookies信息至瀏覽器是非常容易的,這些Cookies將會附加在在HTTP報文頭的Content-type域前。假設你想要以Cookies的方式設置UserID和Password,那么簡單的CGI設置腳本如下:
#include <iostream> using namespace std; int main () { cout << "Set-Cookie:UserID=XYZ;\r\n"; cout << "Set-Cookie:Password=XYZ123;\r\n"; cout << "Set-Cookie:Domain=www.tutorialspoint.com;\r\n"; cout << "Set-Cookie:Path=/perl;\n"; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Cookies in CGI</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "Setting cookies" << endl; cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
從這個例子中你將了解怎么設置Cookies,那就是使用Set-Cookie來設置Cookies。
設置Cookies屬性的時候,Expires, Domain, and Path是可選的,值得注意的一點是Cookies的設置是在發送"Content-type:text/html\r\n\r\n”之前。運行/cgi-bin/setcookies.cgi將會在你的電腦上設置Cookies。
2.獲取Cookies
獲取Cookies也非常簡單,Cookies都存儲在CGI的環境變量HTTP_COOKIE中,並且具有如下的格式:
key1=value1;key2=value2;key3=value3....
以下就是一段獲取Cookies的簡短的CGI代碼:
#include <iostream> #include <vector> #include <string> #include <stdio.h> #include <stdlib.h> #include <cgicc/CgiDefs.h> #include <cgicc/Cgicc.h> #include <cgicc/HTTPHTMLHeader.h> #include <cgicc/HTMLClasses.h> using namespace std; using namespace cgicc; int main () { Cgicc cgi; const_cookie_iterator cci; cout << "Content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>Cookies in CGI</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<table border = \"0\" cellspacing = \"2\">"; // get environment variables const CgiEnvironment& env = cgi.getEnvironment(); for( cci = env.getCookieList().begin(); cci != env.getCookieList().end(); ++cci ) { cout << "<tr><td>" << cci->getName() << "</td><td>"; cout << cci->getValue(); cout << "</td></tr>\n"; } cout << "</table><\n"; cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }