C++ 教程
為了更好地了解 CGI 的概念,讓我們點(diǎn)擊一個(gè)超鏈接,瀏覽一個(gè)特定的網(wǎng)頁(yè)或 URL,看看會(huì)發(fā)生什么。
然而,以這種方式搭建起來(lái)的 HTTP 服務(wù)器,不管何時(shí)請(qǐng)求目錄中的某個(gè)文件,HTTP 服務(wù)器發(fā)送回來(lái)的不是該文件,而是以程序形式執(zhí)行,并把執(zhí)行產(chǎn)生的輸出發(fā)送回瀏覽器顯示出來(lái)。
公共網(wǎng)關(guān)接口(CGI),是使得應(yīng)用程序(稱(chēng)為 CGI 程序或 CGI 腳本)能夠與 Web 服務(wù)器以及客戶(hù)端進(jìn)行交互的標(biāo)準(zhǔn)協(xié)議。這些 CGI 程序可以用 Python、PERL、Shell、C 或 C++ 等進(jìn)行編寫(xiě)。
下圖演示了 CGI 的架構(gòu):
在您進(jìn)行 CGI 編程之前,請(qǐng)確保您的 Web 服務(wù)器支持 CGI,并已配置成可以處理 CGI 程序。所有由 HTTP 服務(wù)器執(zhí)行的 CGI 程序,都必須在預(yù)配置的目錄中。該目錄稱(chēng)為 CGI 目錄,按照慣例命名為 /var/www/cgi-bin。雖然 CGI 文件是 C++ 可執(zhí)行文件,但是按照慣例它的擴(kuò)展名是 .cgi。
默認(rèn)情況下,Apache Web 服務(wù)器會(huì)配置在 /var/www/cgi-bin 中運(yùn)行 CGI 程序。如果您想指定其他目錄來(lái)運(yùn)行 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>
在這里,我們假設(shè)已經(jīng)配置好 Web 服務(wù)器并能成功運(yùn)行,你可以運(yùn)行任意的 CGI 程序,比如 Perl 或 Shell 等。
請(qǐng)看下面的 C++ 程序:
編譯上面的代碼,把可執(zhí)行文件命名為 cplusplus.cgi,并把這個(gè)文件保存在 /var/www/cgi-bin 目錄中。在運(yùn)行 CGI 程序之前,請(qǐng)使用 chmod 755 cplusplus.cgi UNIX 命令來(lái)修改文件模式,確保文件可執(zhí)行。訪(fǎng)問(wèn)可執(zhí)行文件,您會(huì)看到下面的輸出:
上面的 C++ 程序是一個(gè)簡(jiǎn)單的程序,把它的輸出寫(xiě)在 STDOUT 文件上,即顯示在屏幕上。在這里,值得注意一點(diǎn),第一行輸出 Content-type:text/htmlrnrn。這一行發(fā)送回瀏覽器,并指定要顯示在瀏覽器窗口上的內(nèi)容類(lèi)型。您必須理解 CGI 的基本概念,這樣才能進(jìn)一步使用 Python 編寫(xiě)更多復(fù)雜的 CGI 程序。C++ CGI 程序可以與任何其他外部的系統(tǒng)(如 RDBMS)進(jìn)行交互。
行 Content-type:text/htmlrnrn 是 HTTP 頭信息的組成部分,它被發(fā)送到瀏覽器,以便更好地理解頁(yè)面內(nèi)容。HTTP 頭信息的形式如下:
HTTP 字段名稱(chēng): 字段內(nèi)容 例如 Content-type: text/htmlrnrn
還有一些其他的重要的 HTTP 頭信息,這些在您的 CGI 編程中都會(huì)經(jīng)常被用到。
頭信息 | 描述 |
---|---|
Content-type: | MIME 字符串,定義返回的文件格式。例如 Content-type:text/html。 |
Expires: Date | 信息變成無(wú)效的日期。瀏覽器使用它來(lái)判斷一個(gè)頁(yè)面何時(shí)需要刷新。一個(gè)有效的日期字符串的格式應(yīng)為 01 Jan 1998 12:00:00 GMT。 |
Location: URL | 這個(gè) URL 是指應(yīng)該返回的 URL,而不是請(qǐng)求的 URL。你可以使用它來(lái)重定向一個(gè)請(qǐng)求到任意的文件。 |
Last-modified: Date | 資源的最后修改日期。 |
Content-length: N | 要返回的數(shù)據(jù)的長(zhǎng)度,以字節(jié)為單位。瀏覽器使用這個(gè)值來(lái)表示一個(gè)文件的預(yù)計(jì)下載時(shí)間。 |
Set-Cookie: String | 通過(guò) string 設(shè)置 cookie。 |
所有的 CGI 程序都可以訪(fǎng)問(wèn)下列的環(huán)境變量。這些變量在編寫(xiě) CGI 程序時(shí)扮演了非常重要的角色。
變量名 | 描述 |
---|---|
CONTENT_TYPE | 內(nèi)容的數(shù)據(jù)類(lèi)型。當(dāng)客戶(hù)端向服務(wù)器發(fā)送附加內(nèi)容時(shí)使用。例如,文件上傳等功能。 |
CONTENT_LENGTH | 查詢(xún)的信息長(zhǎng)度。只對(duì) POST 請(qǐng)求可用。 |
HTTP_COOKIE | 以鍵 & 值對(duì)的形式返回設(shè)置的 cookies。 |
HTTP_USER_AGENT | 用戶(hù)代理請(qǐng)求標(biāo)頭字段,遞交用戶(hù)發(fā)起請(qǐng)求的有關(guān)信息,包含了瀏覽器的名稱(chēng)、版本和其他平臺(tái)性的附加信息。 |
PATH_INFO | CGI 腳本的路徑。 |
QUERY_STRING | 通過(guò) GET 方法發(fā)送請(qǐng)求時(shí)的 URL 編碼信息,包含 URL 中問(wèn)號(hào)后面的參數(shù)。 |
REMOTE_ADDR | 發(fā)出請(qǐng)求的遠(yuǎn)程主機(jī)的 IP 地址。這在日志記錄和認(rèn)證時(shí)是非常有用的。 |
REMOTE_HOST | 發(fā)出請(qǐng)求的主機(jī)的完全限定名稱(chēng)。如果此信息不可用,則可以用 REMOTE_ADDR 來(lái)獲取 IP 地址。 |
REQUEST_METHOD | 用于發(fā)出請(qǐng)求的方法。最常見(jiàn)的方法是 GET 和 POST。 |
SCRIPT_FILENAME | CGI 腳本的完整路徑。 |
SCRIPT_NAME | CGI 腳本的名稱(chēng)。 |
SERVER_NAME | 服務(wù)器的主機(jī)名或 IP 地址。 |
SERVER_SOFTWARE | 服務(wù)器上運(yùn)行的軟件的名稱(chēng)和版本。 |
下面的 CGI 程序列出了所有的 CGI 變量。
在真實(shí)的實(shí)例中,您需要通過(guò) CGI 程序執(zhí)行許多操作。這里有一個(gè)專(zhuān)為 C++ 程序而編寫(xiě)的 CGI 庫(kù),我們可以從 ftp://ftp.gnu.org/gnu/cgicc/ 上下載這個(gè) CGI 庫(kù),并按照下面的步驟安裝庫(kù):
$ tar xzf cgicc-X.X.X.tar.gz $ cd cgicc-X.X.X/ $ ./configure --prefix=/usr $ make $ make install
注意:libcgicc.so 和 libcgicc.a 庫(kù)會(huì)被安裝到/usr/lib目錄下,需執(zhí)行拷貝命令:
$ sudo cp /usr/lib/libcgicc.* /usr/lib64/才能使 CGI 程序自動(dòng)找到 libcgicc.so 動(dòng)態(tài)鏈接庫(kù)。
您可以點(diǎn)擊 C++ CGI Lib Documentation,查看相關(guān)的庫(kù)文檔。
您可能有遇到過(guò)這樣的情況,當(dāng)您需要從瀏覽器傳遞一些信息到 Web 服務(wù)器,最后再傳到 CGI 程序。通常瀏覽器會(huì)使用兩種方法把這個(gè)信息傳到 Web 服務(wù)器,分別是 GET 和 POST 方法。
GET 方法發(fā)送已編碼的用戶(hù)信息追加到頁(yè)面請(qǐng)求中。頁(yè)面和已編碼信息通過(guò) ? 字符分隔開(kāi),如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET 方法是默認(rèn)的從瀏覽器向 Web 服務(wù)器傳信息的方法,它會(huì)在瀏覽器的地址欄中生成一串很長(zhǎng)的字符串。當(dāng)您向服務(wù)器傳密碼或其他一些敏感信息時(shí),不要使用 GET 方法。GET 方法有大小限制,在一個(gè)請(qǐng)求字符串中最多可以傳 1024 個(gè)字符。
當(dāng)使用 GET 方法時(shí),是使用 QUERY_STRING http 頭來(lái)傳遞信息,在 CGI 程序中可使用 QUERY_STRING 環(huán)境變量來(lái)訪(fǎng)問(wèn)。
您可以通過(guò)在 URL 后跟上簡(jiǎn)單連接的鍵值對(duì),也可以通過(guò)使用 HTML <FORM> 標(biāo)簽的 GET 方法來(lái)傳信息。
下面是一個(gè)簡(jiǎn)單的 URL,使用 GET 方法傳遞兩個(gè)值給 hello_get.py 程序。
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
下面的實(shí)例生成 cpp_get.cgi CGI 程序,用于處理 Web 瀏覽器給出的輸入。通過(guò)使用 C++ CGI 庫(kù),可以很容易地訪(fǎng)問(wèn)傳遞的信息:
現(xiàn)在,編譯上面的程序,如下所示:
$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
生成 cpp_get.cgi,并把它放在 CGI 目錄中,并嘗試使用下面的鏈接進(jìn)行訪(fǎng)問(wèn):
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
這會(huì)產(chǎn)生以下結(jié)果:
名:ZARA 姓:ALI
下面是一個(gè)簡(jiǎn)單的實(shí)例,使用 HTML 表單和提交按鈕傳遞兩個(gè)值。我們將使用相同的 CGI 腳本 cpp_get.cgi 來(lái)處理輸入。
下面是上述表單的實(shí)際輸出,請(qǐng)輸入名和姓,然后點(diǎn)擊提交按鈕查看結(jié)果。
一個(gè)更可靠的向 CGI 程序傳遞信息的方法是 POST 方法。這種方法打包信息的方式與 GET 方法相同,不同的是,它不是把信息以文本字符串形式放在 URL 中的 ? 之后進(jìn)行傳遞,而是把它以單獨(dú)的消息形式進(jìn)行傳遞。該消息是以標(biāo)準(zhǔn)輸入的形式傳給 CGI 腳本的。
我們同樣使用 cpp_get.cgi 程序來(lái)處理 POST 方法。讓我們以同樣的例子,通過(guò)使用 HTML 表單和提交按鈕來(lái)傳遞兩個(gè)值,只不過(guò)這次我們使用的不是 GET 方法,而是 POST 方法,如下所示:
當(dāng)需要選擇多個(gè)選項(xiàng)時(shí),我們使用復(fù)選框。
下面的 HTML 代碼實(shí)例是一個(gè)帶有兩個(gè)復(fù)選框的表單:
下面的 C++ 程序會(huì)生成 cpp_checkbox.cgi 腳本,用于處理 Web 瀏覽器通過(guò)復(fù)選框給出的輸入。
當(dāng)只需要選擇一個(gè)選項(xiàng)時(shí),我們使用單選按鈕。
下面的 HTML 代碼實(shí)例是一個(gè)帶有兩個(gè)單選按鈕的表單:
下面的 C++ 程序會(huì)生成 cpp_radiobutton.cgi 腳本,用于處理 Web 瀏覽器通過(guò)單選按鈕給出的輸入。
當(dāng)需要向 CGI 程序傳遞多行文本時(shí),我們使用 TEXTAREA 元素。
下面的 HTML 代碼實(shí)例是一個(gè)帶有 TEXTAREA 框的表單:
下面的 C++ 程序會(huì)生成 cpp_textarea.cgi 腳本,用于處理 Web 瀏覽器通過(guò)文本區(qū)域給出的輸入。
當(dāng)有多個(gè)選項(xiàng)可用,但只能選擇一個(gè)或兩個(gè)選項(xiàng)時(shí),我們使用下拉框。
下面的 HTML 代碼實(shí)例是一個(gè)帶有下拉框的表單:
下面的 C++ 程序會(huì)生成 cpp_dropdown.cgi 腳本,用于處理 Web 瀏覽器通過(guò)下拉框給出的輸入。
HTTP 協(xié)議是一種無(wú)狀態(tài)的協(xié)議。但對(duì)于一個(gè)商業(yè)網(wǎng)站,它需要在不同頁(yè)面間保持會(huì)話(huà)信息。例如,一個(gè)用戶(hù)在完成多個(gè)頁(yè)面的步驟之后結(jié)束注冊(cè)。但是,如何在所有網(wǎng)頁(yè)中保持用戶(hù)的會(huì)話(huà)信息。
在許多情況下,使用 cookies 是記憶和跟蹤有關(guān)用戶(hù)喜好、購(gòu)買(mǎi)、傭金以及其他為追求更好的游客體驗(yàn)或網(wǎng)站統(tǒng)計(jì)所需信息的最有效的方法。
服務(wù)器以 cookie 的形式向訪(fǎng)客的瀏覽器發(fā)送一些數(shù)據(jù)。如果瀏覽器接受了 cookie,則 cookie 會(huì)以純文本記錄的形式存儲(chǔ)在訪(fǎng)客的硬盤(pán)上。現(xiàn)在,當(dāng)訪(fǎng)客訪(fǎng)問(wèn)網(wǎng)站上的另一個(gè)頁(yè)面時(shí),會(huì)檢索 cookie。一旦找到 cookie,服務(wù)器就知道存儲(chǔ)了什么。
cookie 是一種純文本的數(shù)據(jù)記錄,帶有 5 個(gè)可變長(zhǎng)度的字段:
向?yàn)g覽器發(fā)送 cookies 是非常簡(jiǎn)單的。這些 cookies 會(huì)在 Content-type 字段之前,與 HTTP 頭一起被發(fā)送。假設(shè)您想設(shè)置 UserID 和 Password 為 cookies,設(shè)置 cookies 的步驟如下所示:
從這個(gè)實(shí)例中,我們了解了如何設(shè)置 cookies。我們使用 Set-Cookie HTTP 頭來(lái)設(shè)置 cookies。
在這里,有一些設(shè)置 cookies 的屬性是可選的,比如 Expires、Domain 和 Path。值得注意的是,cookies 是在發(fā)送行 "Content-type:text/htmlrnrn 之前被設(shè)置的。
編譯上面的程序,生成 setcookies.cgi,并嘗試使用下面的鏈接設(shè)置 cookies。它會(huì)在您的計(jì)算機(jī)上設(shè)置四個(gè) cookies:
/cgi-bin/setcookies.cgi
檢索所有設(shè)置的 cookies 是非常簡(jiǎn)單的。cookies 被存儲(chǔ)在 CGI 環(huán)境變量 HTTP_COOKIE 中,且它們的形式如下:
key1=value1;key2=value2;key3=value3....
下面的實(shí)例演示了如何獲取 cookies。
現(xiàn)在,編譯上面的程序,生成 getcookies.cgi,并嘗試使用下面的鏈接獲取您的計(jì)算機(jī)上所有可用的 cookies:
/cgi-bin/getcookies.cgi
這會(huì)產(chǎn)生一個(gè)列表,顯示了上一節(jié)中設(shè)置的四個(gè) cookies 以及您的計(jì)算機(jī)上所有其他的 cookies:
UserID XYZ Password XYZ123 Domain Path /perl
為了上傳一個(gè)文件,HTML 表單必須把 enctype 屬性設(shè)置為 multipart/form-data。帶有文件類(lèi)型的 input 標(biāo)簽會(huì)創(chuàng)建一個(gè) "Browse" 按鈕。
這段代碼的結(jié)果是下面的表單:
注意:上面的實(shí)例已經(jīng)故意禁用了保存上傳的文件在我們的服務(wù)器上。您可以在自己的服務(wù)器上嘗試上面的代碼。
下面是用于處理文件上傳的腳本 cpp_uploadfile.cpp:
上面的實(shí)例是在 cout 流中寫(xiě)入內(nèi)容,但您可以打開(kāi)文件流,并把上傳的文件內(nèi)容保存在目標(biāo)位置的某個(gè)文件中。