?? 203.htm
字號:
<p>用VB編寫標準CGI程序(上)</p>
<p></p>
<p> </p>
<p>利用通用網(wǎng)關(guān)接口(CGI),Web服務(wù)器可以執(zhí)行一些外部程序,并將這些外部程序所產(chǎn)生的輸出結(jié)果和Web服務(wù)器所管理的靜態(tài)文本、圖像和聲音融合在一起傳給相應(yīng)的Web瀏覽器。當客戶機的瀏覽器向Web服務(wù)器請求一個HTML文件時,服務(wù)器在收到請求后就去尋找這個文件并將找到的文件傳送給客戶機。而當客戶機的請求是一個CGI程序時,Web服務(wù)器將激活客戶機所請求的CGI程序并把程序的執(zhí)行結(jié)果傳給客戶機。 </p>
<p></p>
<p> 標準的CGI程序是通過環(huán)境變量和標準輸入輸出來與Web服務(wù)器交換信息的。任何一個被系統(tǒng)激活的進程都擁有標準輸入和輸出這兩個文件句柄,CGI程序的進程也不例外。不過,當CGI程序被Web服務(wù)器激活以后,它的標準輸入STDIN被連接到Web服務(wù)器的標準輸出STDOUT上,而CGI程序的標準輸出STDOUT則被連到服務(wù)器的標準輸入STDIN上。因此,CGI程序從標準輸入讀取信息(也就是從Web服務(wù)器的標準輸出讀信息),而它向標準輸出寫信息(也就是向Web服務(wù)器的標準輸入寫信息)。 </p>
<p></p>
<p> Web服務(wù)器一般將客戶機傳送來的信息放在它的標準輸出和相關(guān)環(huán)境變量中,而CGI程序則從環(huán)境變量和它的標準輸入(也就是Web服務(wù)器的標準輸出)獲取所需的信息,程序的最終輸出結(jié)果則被寫向它的標準輸出STDOUT(也就是Web服務(wù)器的標準輸入)。Web服務(wù)器將從它的標準輸入STDIN(也就是CGI程序的標準輸出)獲取CGI程序的輸出結(jié)果并將它傳送給客戶機。客戶機、Web服務(wù)器和CGI程序之間的信息交流如下圖所示。顯然,Web服務(wù)器就像是客戶機和CGI程序間的中介。 </p>
<p></p>
<p> Web服務(wù)器、CGI程序間的這種標準框架在Unix系統(tǒng)下和微軟Windows環(huán)境中的字符方式下可以工作得非常好,因為此時系統(tǒng)產(chǎn)生的所有進程都可以存取標準輸入和標準輸出。但對于微軟Windows圖形方式下的程序就不行了,因為它們無法存取標準輸入和標準輸出。為了解決這一問題,微軟在Win32系統(tǒng)中創(chuàng)建了另一類型的標準輸入和標準輸出,程序可以通過調(diào)用Win32 API函數(shù)來存取標準輸入和標準輸出,不過,這就意味著使用這類標準輸入和標準輸出的CGI程序都必須是32位的。 </p>
<p></p>
<p> 微軟Windows環(huán)境下的其它一些Web服務(wù)器(例如Website)則使用另外一種特殊的技術(shù)(即利用INI文件)來實現(xiàn)Web服務(wù)器和CGI程序間的數(shù)據(jù)交流。采用這種被稱為“Win-CGI”規(guī)范編寫的CGI程序通常只能在部分Web服務(wù)器上運行。一般地,支持Win-CGI的Web服務(wù)器將客戶端的輸入以及有關(guān)的狀態(tài)信息寫入到一個INI文件中,而CGI程序則從該INI文件中獲取相關(guān)信息,這類程序的執(zhí)行效率沒有標準CGI程序高。 </p>
<p></p>
<p> 在進行CGI編程時,只要使CGI程序從標準輸入和環(huán)境變量中獲取客戶機提供的信息,并將要傳送給客戶機的輸出結(jié)果寫入標準輸出,剩下的信息傳遞工作將由Web服務(wù)器自動完成。CGI只是規(guī)定了一個標準的接口規(guī)范,只要遵守這個標準規(guī)范,程序開發(fā)人員就可以利用各種編程工具(如Perl、C、FORTRAN、Visual Basic等)進行CGI編程了。考慮到Visual Basic的強大的數(shù)據(jù)庫處理能力、客戶機/服務(wù)器模式的編程能力以及字符串處理能力,所以本文主要向大家介紹如何使用VB編寫標準的CGI程序。 </p>
<p></p>
<p> 一、輸入輸出的處理 </p>
<p></p>
<p> 一個CGI程序被激活以后,它首先要做的事情就是確定系統(tǒng)平臺、Web服務(wù)器和客戶端瀏覽器的狀態(tài)信息以及客戶端用戶的輸入數(shù)據(jù)。此外,它還必須能夠?qū)⑾嚓P(guān)信息傳送給客戶端,否則它將一事無成。這些操作都是通過存取環(huán)境變量和標準輸入輸出來完成的。用VB編寫的CGI程序通過調(diào)用函數(shù)Environ( )來獲取相關(guān)環(huán)境變量的值。存取標準輸入輸出就要在程序中使用Win32API函數(shù)GetStdHandle( )、ReadFile( )和WriteFile( ),在使用這些函數(shù)時首先必須在程序中聲明它們,寫聲明語句時可以借助于VB提供的API文本查看器。 </p>
<p></p>
<p> 以下的CGI程序說明了在VB-CGI程序中如何處理環(huán)境變量和標準輸入輸出。該CGI程序非常簡單,可將標準輸入中的信息不經(jīng)任何處理就返回給客戶端,它可被任何表單用POST方法激活: </p>
<p></p>
<p> Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Long) As Long </p>
<p></p>
<p> Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, </p>
<p></p>
<p> ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long,lpOverlapped As Any) As Long </p>
<p></p>
<p> Declare Function WriteFile Lib "kernel32" (ByVal hFile As Long, ByVal lpBuffer As String,ByVal nNumberOfBytesToWrite As Long, lpNumberOfBytesWritten As Long,lpOverlapped As Any) As Long </p>
<p></p>
<p> Public Const STD_INPUT_HANDLE = -10& </p>
<p></p>
<p> Public Const STD_OUTPUT_HANDLE = -11& </p>
<p></p>
<p> Public Const FILE_BEGIN = 0& </p>
<p></p>
<p> Public hStdIn As Long 注釋: 標準輸入文件句柄 </p>
<p></p>
<p> Public hStdOut As Long 注釋: 標準輸出文件句柄 </p>
<p></p>
<p> </p>
<p></p>
<p> Sub Main() </p>
<p></p>
<p> Dim CGI_ContentLength As String,CGI_QueryString As String </p>
<p></p>
<p> Dim lContentLength As Long 注釋: 標準輸入中的字符串的長度 </p>
<p></p>
<p> Dim sBuff As String 注釋: 用于存儲標準輸入中的字符串 </p>
<p></p>
<p> Dim lBytesRead As Long 注釋: 實際讀入的字符個數(shù) </p>
<p></p>
<p> Dim rc As Long </p>
<p></p>
<p> Dim sFormData As String </p>
<p></p>
<p> 注釋:調(diào)用系統(tǒng)函數(shù)生成標準輸入輸出文件句柄 </p>
<p></p>
<p> hStdIn = GetStdHandle(STD_INPUT_HANDLE) </p>
<p></p>
<p> hStdOut = GetStdHandle(STD_OUTPUT_HANDLE) </p>
<p></p>
<p> 注釋:獲取環(huán)境變量CONTENT_LENGTH的值,并將它轉(zhuǎn)換為整型 </p>
<p></p>
<p> CGI_ContentLength = Environ("CONTENT_LENGTH") </p>
<p></p>
<p> lContentLength = Val(CGI_ContentLength) </p>
<p></p>
<p> sBuff = String(lContentLength, Chr$(0)) </p>
<p></p>
<p> 注釋:從標準輸入中讀數(shù)據(jù) </p>
<p></p>
<p> rc = ReadFile(hStdIn, ByVal sBuff, lContentLength, lBytesRead, ByVal 0&) </p>
<p></p>
<p> sFormData = Left$(sBuff, lBytesRead) </p>
<p></p>
<p> OutPut "Content-type: text/html" & vbCrLf </p>
<p></p>
<p> OutPut "<HTML><HEAD>" </p>
<p></p>
<p> OutPut "<TITLE>表單傳送數(shù)據(jù)的方法POST </TITLE></HEAD>" </p>
<p></p>
<p> OutPut "<BODY><H3>表單傳送數(shù)據(jù)的方法POST</H3> " </p>
<p></p>
<p> OutPut "<P>本CGI 程序使用Visual Basic編制! " </p>
<p></p>
<p> OutPut "<P>POST方法傳送的數(shù)據(jù): " </p>
<p></p>
<p> OutPut "<P>" & sBuff </p>
<p></p>
<p> OutPut "</BODY></HTML>" </p>
<p></p>
<p> End Sub </p>
<p></p>
<p> Sub OutPut(s As String) 注釋: 定義一個向標準輸出寫信息的函數(shù) </p>
<p></p>
<p> Dim lBytesWritten As Long </p>
<p></p>
<p> s = s & vbCrLf </p>
<p></p>
<p> WriteFile hStdOut, s, Len(s), lBytesWritten, ByVal 0& </p>
<p></p>
<p> End Sub </p>
<p></p>
<p> 一般地,用VB編譯生成的CGI程序不能正確處理中文信息。這主要表現(xiàn)在CGI程序向STDOUT輸出的中文在Web頁面上無法正確顯示,可通過在該中文字符串后跟著輸出一些空格來解決這個問題。當使用HTML標識符<P>、</P>對Web頁面進行排版時,瀏覽器在顯示該Web頁面時會吃掉多余的空格而只保留一個。在這種情況下,這些空格對Web頁面的外觀基本上沒有什么影響。如果使用HTML標識符<PRE>、</PRE>對Web頁面進行排版,則由于空格不能被瀏覽器吃掉,所以Web頁面的外觀將會受到較大的影響。不過,這時可用HTML的表格<table>、<P>來代替<PRE>對Web頁面進行排版。 </p>
<p></p>
<p> 注意:整個CGI程序的主體必須放在MAIN()函數(shù)中。 </p>
<p></p>
<p> 二、URL譯碼與解碼 </p>
<p></p>
<p> 由于Web服務(wù)器和瀏覽器不能正確處理一些特殊的字符,Web服務(wù)器和瀏覽器之間可能會因此而產(chǎn)生某種程度的誤會,所以在數(shù)據(jù)被傳送之前,瀏覽器都要對表單內(nèi)客戶輸入的數(shù)據(jù)中的特殊字符進行URL譯碼。 </p>
<p></p>
<p> 例如,Web系統(tǒng)用“=”分解表單各元素的NAME和VALUE屬性,用“&”分解不同表單元素的輸入數(shù)據(jù)。如果在表單的輸入數(shù)據(jù)中包含這些特殊的字符,并且表單的數(shù)據(jù)在傳送給Web服務(wù)器前不作任何處理,則Web服務(wù)器將無法知道哪一個“=”、“&”是用戶輸入的,哪一個是瀏覽器加上的。在由表單屬性ACTION定義的URL中,也可能會出現(xiàn)一些特殊的字符,當在CGI程序的名稱和路徑信息(Path Information)中出現(xiàn)“=”、“&”和“?”時,都會影響數(shù)據(jù)的正確傳送。 </p>
<p></p>
<p> URL譯碼(URL Encoding)就是將Web服務(wù)器所不能正確處理的特殊字符轉(zhuǎn)換成它的十六進制數(shù)的形式,比如將“%”轉(zhuǎn)換成“%25”、“=”轉(zhuǎn)換成“%3D”等等。這些特殊的字符通常被稱作Web系統(tǒng)的保留字符。在Web系統(tǒng)上無論是用GET方法還是用POST方法傳送的數(shù)據(jù)都要進行URL譯碼。CGI程序要想處理表單傳送來的數(shù)據(jù),還必須對瀏覽器URL譯碼過的數(shù)據(jù)進行解碼。因此,理解URL譯碼對于我們進行CGI編程是非常重要的。URL譯碼一般包括以下步驟: </p>
<p></p>
<p> 1、瀏覽器將所傳送的數(shù)據(jù)根據(jù)表單所包含的元素分解成“NAME=VALUE”形式,NAME和VALUE分別是表單元素的屬性。其中,VALUE屬性中存儲客戶機在表單中輸入的數(shù)據(jù):如果客戶機沒有輸入數(shù)據(jù),則VALUE存儲的是表單定義的缺省值;如果缺省值也沒有定義,則VALUE值為空。 </p>
<p></p>
<p> 2、代表表單中各元素的各個“NAME=VALUE”對被瀏覽器用“&”連接起來。 </p>
<p></p>
<p> 3、VALUE屬性中存放的數(shù)據(jù)若含有空格,則被轉(zhuǎn)換成“+”。 </p>
<p></p>
<p> 4、URL和輸入數(shù)據(jù)中所包含的Web系統(tǒng)的保留字符必須被譯碼成其十六進制數(shù)形式。 </p>
<p></p>
<p> 5、被譯碼后的字符被表示成一個“%”和它們的十六進制數(shù)形式(即%HH)。 </p>
<p></p>
<p> CGI程序從環(huán)境變量“QUERY_STRING”或標準輸入中讀入的數(shù)據(jù)是經(jīng)過瀏覽器URL譯碼過的,故在使用這些數(shù)據(jù)以前還必須對它們進行URL解碼。解碼的目的是將數(shù)據(jù)還原成客戶端用戶在Web頁面上輸入時的形式。本文已經(jīng)介紹了URL譯碼過程,URL解碼過程與它正好相反,它一般包括以下步驟: </p>
<p></p>
<p> 1、從瀏覽器用GET或POST方法所傳送來的數(shù)據(jù)中找出代表各個表單元素所儲存數(shù)據(jù)的“NAME=VALUE”對。 </p>
<p></p>
<p> 2、VALUE屬性中所存放的數(shù)據(jù)若含有“+”,則被轉(zhuǎn)換成空格。 </p>
<p></p>
<p> 3、將VALUE屬性中所存放的數(shù)據(jù)的十六進制數(shù)“%HH”轉(zhuǎn)換成相應(yīng)的字符。 </p>
<p></p>
<p> Web系統(tǒng)將漢字當成特殊的字符,對它也要進行URL譯碼。對于一個特殊的單字節(jié)字符(比如“/”),瀏覽器通常將它譯碼成十六進制數(shù)的形式(比如%2F),“%”表示它后面跟的是兩位十六進制數(shù)。當VB程序?qū)ζ溥M行處理時調(diào)用Chr$函數(shù)就可以將其恢復為原貌。而一個漢字則被瀏覽器譯碼成四位十六進制數(shù)(比如%D5%C5)。如果CGI程序還像以前那樣分別調(diào)用Chr(D5)和Chr(C5),則由于D5、C5都不是正常的單字節(jié)十六進制數(shù)碼,故Chr函數(shù)返回空,漢字將無法正確還原。正確的做法應(yīng)該是將有關(guān)漢字的四位十六進制數(shù)一起傳給函數(shù)Chr(如Chr(D5C5)),此時漢字才能被正確還原。 </p>
<p></p>
<p> 因此,可以讓CGI程序?qū)λ奈贿B續(xù)的十六進制數(shù)一起進行譯碼,以便使?jié)h字能夠被正確還原。但在這種情況下,當客戶端用戶輸入了兩個連續(xù)的Web系統(tǒng)保留字符時,CGI程序又可能把它們當成漢字來處理。這時可以讓CGI程序在需要對四位連續(xù)的十六進制數(shù)進行譯碼時首先檢查前面兩位是否為Web系統(tǒng)的保留字符,如果是則仍然按照單字節(jié)的字符處理。不過如果客戶端用戶在表單內(nèi)填寫了很多漢字,則CGI程序的負擔將會大大加重。事實上,在大多數(shù)情況下,客戶端用戶很少會使用兩個連續(xù)的Web系統(tǒng)的保留字符,所以可以只讓CGI程序?qū)ψ钊菀壮霈F(xiàn)的情形如“://”(當客戶端用戶在表單中輸入某一URL時會出現(xiàn)這種情況)進行檢查,本文下節(jié)提供的函數(shù)UrlDecode( )可以實現(xiàn)對漢字和Web系統(tǒng)保留字符的URL解碼。</p>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -