c++ web編程
1. 什么是 cgi?
- 公共網關接口(cgi),是一套標準,定義了信息是如何在 web 服務器和客戶端腳本之間進行交換的。
- cgi 規范目前是由 ncsa 維護的,ncsa 定義 cgi 如下:
- 公共網關接口(cgi),是一種用于外部網關程序與信息服務器(如 http 服務器)對接的接口標準。
- 目前的版本是 cgi/1.1,cgi/1.2 版本正在推進中。
2. web 瀏覽
為了更好地了解 cgi 的概念,讓我們點擊一個超鏈接,瀏覽一個特定的網頁或 url,看看會發生什么。
- 您的瀏覽器聯系上 http web 服務器,并請求 url,即文件名。
- web 服務器將解析 url,并查找文件名。如果找到請求的文件,web 服務器會把文件發送回瀏覽器,否則發送一條錯誤消息,表明您請求了一個錯誤的文件。
- web 瀏覽器從 web 服務器獲取響應,并根據接收到的響應來顯示文件或錯誤消息。
然而,以這種方式搭建起來的 http 服務器,不管何時請求目錄中的某個文件,http 服務器發送回來的不是該文件,而是以程序形式執行,并把執行產生的輸出發送回瀏覽器顯示出來。
公共網關接口(cgi),是使得應用程序(稱為 cgi 程序或 cgi 腳本)能夠與 web 服務器以及客戶端進行交互的標準協議。這些 cgi 程序可以用 python、perl、shell、c 或 c++ 等進行編寫。
3. cgi 架構圖
下圖演示了 cgi 的架構:
4. web 服務器配置
在您進行 cgi 編程之前,請確保您的 web 服務器支持 cgi,并已配置成可以處理 cgi 程序。所有由 http 服務器執行的 cgi 程序,都必須在預配置的目錄中。該目錄稱為 cgi 目錄,按照慣例命名為 /var/www/cgi-bin。雖然 cgi 文件是 c++ 可執行文件,但是按照慣例它的擴展名是 .cgi。
默認情況下,apache web 服務器會配置在 /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>
在這里,我們假設已經配置好 web 服務器并能成功運行,你可以運行任意的 cgi 程序,比如 perl 或 shell 等。
5. 第一個 cgi 程序
請看下面的 c++ 程序:
#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 - 第一個 cgi 程序</title>\n"; cout << "</head>\n"; cout << "<body>\n"; cout << "<h2>hello world! 這是我的第一個 cgi 程序</h2>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
編譯上面的代碼,把可執行文件命名為 cplusplus.cgi,并把這個文件保存在 /var/www/cgi-bin 目錄中。在運行 cgi 程序之前,請使用 chmod 755 cplusplus.cgi unix 命令來修改文件模式,確保文件可執行。訪問可執行文件,您會看到下面的輸出:
6. hello world! 這是我的第一個 cgi 程序
上面的 c++ 程序是一個簡單的程序,把它的輸出寫在 stdout 文件上,即顯示在屏幕上。在這里,值得注意一點,第一行輸出 content-type:text/html\r\n\r\n。這一行發送回瀏覽器,并指定要顯示在瀏覽器窗口上的內容類型。您必須理解 cgi 的基本概念,這樣才能進一步使用 python 編寫更多復雜的 cgi 程序。c++ cgi 程序可以與任何其他外部的系統(如 rdbms)進行交互。
7. http 頭信息
行 content-type:text/html\r\n\r\n 是 http 頭信息的組成部分,它被發送到瀏覽器,以便更好地理解頁面內容。http 頭信息的形式如下:
http 字段名稱: 字段內容 例如 content-type: text/html\r\n\r\n
還有一些其他的重要的 http 頭信息,這些在您的 cgi 編程中都會經常被用到。
頭信息 | 描述 |
---|---|
content-type: | mime 字符串,定義返回的文件格式。例如 content-type:text/html。 |
expires: date | 信息變成無效的日期。瀏覽器使用它來判斷一個頁面何時需要刷新。一個有效的日期字符串的格式應為 01 jan 1998 12:00:00 gmt。 |
location: url | 這個 url 是指應該返回的 url,而不是請求的 url。你可以使用它來重定向一個請求到任意的文件。 |
last-modified: date | 資源的最后修改日期。 |
content-length: n | 要返回的數據的長度,以字節為單位。瀏覽器使用這個值來表示一個文件的預計下載時間。 |
set-cookie: string | 通過 string 設置 cookie。 |
8. cgi 環境變量
所有的 cgi 程序都可以訪問下列的環境變量。這些變量在編寫 cgi 程序時扮演了非常重要的角色。
變量名 | 描述 |
---|---|
content_type | 內容的數據類型。當客戶端向服務器發送附加內容時使用。例如,文件上傳等功能。 |
content_length | 查詢的信息長度。只對 post 請求可用。 |
http_cookie | 以鍵 & 值對的形式返回設置的 cookies。 |
http_user_agent | 用戶代理請求標頭字段,遞交用戶發起請求的有關信息,包含了瀏覽器的名稱、版本和其他平臺性的附加信息。 |
path_info | cgi 腳本的路徑。 |
query_string | 通過 get 方法發送請求時的 url 編碼信息,包含 url 中問號后面的參數。 |
remote_addr | 發出請求的遠程主機的 ip 地址。這在日志記錄和認證時是非常有用的。 |
remote_host | 發出請求的主機的完全限定名稱。如果此信息不可用,則可以用 remote_addr 來獲取 ip 地址。 |
request_method | 用于發出請求的方法。最常見的方法是 get 和 post。 |
script_filename | cgi 腳本的完整路徑。 |
script_name | cgi 腳本的名稱。 |
server_name | 服務器的主機名或 ip 地址。 |
server_software | 服務器上運行的軟件的名稱和版本。 |
下面的 cgi 程序列出了所有的 cgi 變量。
#include <iostream> #include <stdlib.h> 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 環境變量</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>"; // 嘗試檢索環境變量的值 char *value = getenv( env[ i ].c_str() ); if ( value != 0 ){ cout << value; }else{ cout << "環境變量不存在。"; } cout << "</td></tr>\n"; } cout << "</table><\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }
9. c++ cgi 庫
在真實的實例中,您需要通過 cgi 程序執行許多操作。這里有一個專為 c++ 程序而編寫的 cgi 庫,我們可以從 ftp://ftp.gnu.org/gnu/cgicc/ 上下載這個 cgi 庫,并按照下面的步驟安裝庫:
$tar xzf cgicc-x.x.x.tar.gz $cd cgicc-x.x.x/ $./configure --prefix=/usr $make $make install
您可以點擊 c++ cgi lib documentation,查看相關的庫文檔。
10. get 和 post 方法
您可能有遇到過這樣的情況,當您需要從瀏覽器傳遞一些信息到 web 服務器,最后再傳到 cgi 程序。通常瀏覽器會使用兩種方法把這個信息傳到 web 服務器,分別是 get 和 post 方法。
11. 使用 get 方法傳遞信息
get 方法發送已編碼的用戶信息追加到頁面請求中。頁面和已編碼信息通過 ? 字符分隔開,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
get 方法是默認的從瀏覽器向 web 服務器傳信息的方法,它會在瀏覽器的地址欄中生成一串很長的字符串。當您向服務器傳密碼或其他一些敏感信息時,不要使用 get 方法。get 方法有大小限制,在一個請求字符串中最多可以傳 1024 個字符。
當使用 get 方法時,是使用 query_string http 頭來傳遞信息,在 cgi 程序中可使用 query_string 環境變量來訪問。
您可以通過在 url 后跟上簡單連接的鍵值對,也可以通過使用 html <form> 標簽的 get 方法來傳信息。
12. 簡單的 url 實例:get 方法
下面是一個簡單的 url,使用 get 方法傳遞兩個值給 hello_get.py 程序。
/cgi-bin/cpp_get.cgi?first_name=zara&last_name=ali
下面的實例生成 cpp_get.cgi cgi 程序,用于處理 web 瀏覽器給出的輸入。通過使用 c++ 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 formdata; cout << "content-type:text/html\r\n\r\n"; cout << "<html>\n"; cout << "<head>\n"; cout << "<title>使用 get 和 post 方法</title>\n"; cout << "</head>\n"; cout << "<body>\n"; form_iterator fi = formdata.getelement("first_name"); if( !fi->isempty() && fi != (*formdata).end()) { cout << "名:" << **fi << endl; }else{ cout << "no text entered for first name" << endl; } cout << "<br/>\n"; fi = formdata.getelement("last_name"); if( !fi->isempty() &&fi != (*formdata).end()) { cout << "姓:" << **fi << endl; }else{ cout << "no text entered for last name" << endl; } cout << "<br/>\n"; cout << "</body>\n"; cout << "</html>\n"; return 0; }