linux下的webserver BOA及CGIC庫的使用指南(轉帖)
我把網頁掛載到nfs 下面的文件中(需要新建一個文件www ),不過這樣很方便!
安裝過程
==========================================================
1 )在www.boa.org 下載boa-0.94.13.tar.gz 并解壓
# tar -zxvf boa-0.94.13.tar.gz
2 )在src 目錄下運行./configure
3 )生成Makefile 文件,修改
CC = arm-linux-gcc
CPP = arm-linux-gcc–E
(1) 將boa.c 文件中以下幾行判斷去掉即可。
if ( setuid ( 0 ) != - 1 ) {
DIE ( "icky Linux kernel bug!" );
}
(2)
修改文件compat.h P120
#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
修改成
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff
(3)
把src 文件夾下的config.c 里的if(!server_name){..........} (大概在266 行到286 行之間)注釋掉
否則
Error :
./boa
gethostbyname:: Resource temporarily unavailable
(4) 修改 src/log.c
注釋掉
if (dup2(error_log, STDERR_FILENO) == -1) {
DIE("unable to dup2 the error log");
}
否則會出現錯誤:
log.c:73 unable to dup2 the error log:bad file descriptor
改變板子的屬性,為可寫:執行一個命令就可以變成可寫的 好象是chmod 777
自動啟動boa :在/etc/profile 中啟動boa 即可
4 )make
5 )執行arm-linux-strip boa
去掉調試信息,小很多,50 多k
可以編譯出boa 可執行文件,下面是對文件系統的修改
(1 )建立/etc/boa/boa.conf 可以從boa 源碼里拷貝boa.conf
(2 )修改boa.conf 文件,以下為轉載
**********************************************************
# 監聽的端口號,缺省都是80 ,一般無需修改
Port 80
# bind 調用的IP 地址,一般注釋掉,表明綁定到INADDR_ANY ,通配于服務器的所有IP 地址
#Listen 192.68.0.5
User 0
Group 0
# 當服務器發生問題時發送報警的email 地址,目前未用,注釋掉
#ServerAdmin root@localhost
# 錯誤日志文件。如果沒有以/ 開始,則表示從服務器的根路徑開始。如果不需要錯誤日志,則用#/dev/null 。在下面設置時,注意一定要建立/var/log/boa 目錄
ErrorLog /mnt/log/boa/error_log
# 訪問日志文件。如果沒有以/ 開始,則表示從服務器的根路徑開始。如果不需要錯誤日志,則用#/dev/null 或直接注釋掉。在下面設置時,注意一定要建立/var/log/boa 目錄
#AccessLog /var/log/boa/access_log
# 是否使用本地時間。如果沒有注釋掉,則使用本地時間。注釋掉則使用UTC 時間
#UseLocaltime
# 是否記錄CGI 運行信息,如果沒有注釋掉,則記錄,注釋掉則不記錄
#VerboseCGILogs
# 服務器名字
ServerName www.hyesco.com
# 是否啟動虛擬主機功能,即設備可以有多個網絡接口,每個接口都可以擁有一個虛擬的Web 服
# 務器。一般注釋掉,即不需要啟動
#VirtualHost
# 非常重要,HTML 文檔的主目錄。如果沒有以/ 開始,則表示從服務器的根路徑開始。
DocumentRoot /var/www
# 如果收到一個用戶請求的話,在用戶主目錄后再增加的目錄名
UserDir public_html
#HTML 目錄索引的文件名,也是沒有用戶只指明訪問目錄時返回的文件名
DirectoryIndex index.html
# 當HTML 目錄沒有索引文件時,用戶只指明訪問目錄時,boa 會調用該程序生成索引文件然后
# 返回給用戶,因為該過程比較慢最好不執行,可以注釋掉或者給每個HTML 目錄加上#DirectoryIndex 指明的文件
#DirectoryMaker /usr/lib/boa/boa_indexer
# 如果DirectoryIndex 不存在,并且DirectoryMaker 被注釋,那么就用Boa 自帶的索引
# 生成程序來生成目錄的索引文件并輸出到下面目錄,該目錄必須是Boa 能讀寫
# DirectoryCache /var/spool/boa/dircache
# 一個連接所允許的HTTP 持續作用請求最大數目,注釋或設為0 都將關閉HTTP 持續作用
KeepAliveMax 1000
#HTTP 持續作用中服務器在兩次請求之間等待的時間數,以秒為單位,超時將關閉連接
KeepAliveTimeout 10
# 指明mime.types 文件位置。如果沒有以/ 開始,則表示從服務器的根路徑開始。可以注釋掉
# 避免使用mime.types 文件,此時需要用AddType 在本文件里指明
MimeTypes /etc/mime.types
# 文件擴展名沒有或未知的話,使用的缺省MIME 類型
DefaultType text/plain
# 提供CGI 程序的PATH 環境變量值
CGIPath /bin:/usr/bin:/usr/local/bin
# 將文件擴展名和MIME 類型關聯起來,和mime.types 文件作用一樣。如果用mime.types
# 文件,則注釋掉,如果不使用mime.types 文件,則必須使用
#AddType application/x-httpd-cgi cgi
# 指明文檔重定向路徑
#Redirect /bar http://elsewhere/feh/bar
# 為路徑加上別名
Alias /doc /usr/doc
# 非常重要,指明CGI 腳本的虛擬路徑對應的實際路徑。一般所有的CGI 腳本都要放在實際路徑
# 里,用戶訪問執行時輸入站點+ 虛擬路徑+CGI 腳本名
ScriptAlias /cgi-bin/ /mnt/www/cgi-bin/
用戶可以根據自己需要,對boa.conf 進行修改,但必須要保證其他的輔助文件和設置必須和boa.conf 里的配置相符,不然Boa 就不能正常工作。 在上面的例子中,我們還需要創建日志文件所在目錄/mnt/log/boa ,創建HTML 文檔的主目錄/mnt/www ,將mime.types 文件拷貝 到/etc 目錄,創建CGI 腳本所在目錄/var/mnt/cgi-bin/ 。mime.types 文件用來指明不同文件擴展名對應的MIME 類型,一般 可以直接從Linux 主機上拷貝一個,大部分也都是在主機的/etc 目錄下。
**********************************************************
我做的修改
DocumentRoot /mnt/www www 目錄,需手動建立
ScriptAlias /cgi-bin/ /mnt/www/cgi-bin/ www/cgi-bin 目錄,需手動建立
Group 0
(3 )etc 目錄里還要有passwd group mime.types 等文件
www 目錄放index.html 文件
我把passwd group mime.types 放在/etc 下面,boa.conf 放在/etc/boa 文件夾下, 可執行文件boa 放在/bin 下面,文件系統新添加了mnt ,把www 和cgi-bin 放進去,編譯文件系統CRAMFS ,燒錄…
(注:這樣燒錄的話,文件都是只讀的,可以把1: /bin/mkdir /tmp/fs
2:/bin/mount -t yaffs /dev/mtdblock/3 /tmp/fs;
這樣就mount 上一個可讀可寫的文件,掉電還在;問題是:我mount 出錯,有bad block )
(4 )在板子上運行boa
(5 )pc 機用IE 訪問 http://192.168.3.223/index.html
(2 )
編輯helloworld.c 程序測試cgi 的運行
#arm-linux-gcc -o helloworld.cgi helloworld.c
#cp helloworld.cgi 到開發板的/var/www/cgi-bin 目錄下
在pc 機的瀏覽器地址欄輸入http://192.168.0.12/cgi-bin/helloworld.cgi ,可以看到相關頁面,CGI 腳本測試通過。
10. 從CGIC 的主站點http://www.boutell.com/cgic/ 下載源碼,將其解壓并進入源碼目錄
# tar -zxvf cgic205.tar.gz
# cd cgic205
11. 修改Makefile 文件
a. 找到CC=gcc ,將其改成CC=arm-linux-gcc ,
b. 找到AR=ar ,將其改成AR=arm-linux-ar ,
c. 找到RANLIB=ranlib ,將其改成RANLIB=arm-linux-ranlib 。
e. 找到gcc cgictest.o -o cgictest.cgi ${LIBS} ,
將其改成$(CC) $(CFLAGS) cgictest.o -o cgictest.cgi ${LIBS} ,
f. 找到gcc capture.o -o capture ${LIBS} ,
將其改成$(CC) $(CFLAGS) capture.o -o capture ${LIBS} ,
保存退出。
12. 然后運行make 進行編譯,得到的CGIC 庫libcgic.a ,我們通過調試輔助程序capture 和測試程序cgictest.cgi ,來驗證生成CGIC 庫的正確性。
13. 將capture 和cgictest.cgi 拷貝到主機的/var/www/cgi-bin 目錄下。
在工作站的瀏覽器地址欄輸入http://192.168.0.12/cgi-bin/cgictest.cgi ,可以看到頁面,CGIC 庫和測試腳本都移植成功。
將.cgi 文件拷貝至目標板上后,必須改變其權限
chmod 755 *
否則,上位機瀏覽時會提示
502 Bad Gateway
The CGI was not CGI / 1 . 1 compliant .
(2 )
不能上傳的話,把GET 改成POST
把用戶權限由 nobody 改成 root
用 C/C++ 寫 CGI 程序
其實用 C/C++ 寫 CGI 程序非常簡單,主要是要清楚什么是 CGI 。
CGI 全稱 Common Gateway Interface (共同編程接口),是一種編程接口,不論什么語言,只要按照該接口的標準編寫出來的程序,即可叫做 CGI 程序。 CGI 程序的輸入 / 輸出是使用編程語言的標準輸入 / 標準輸出,所以用 C/C++ 來寫 CGI 程序就好象寫普通程序一樣,不過還有幾樣東西要注意的。
1 、 CGI 程序的通信方式
當 有數據從瀏覽器傳到 Web 服務器后,該服務器會根據傳送的類型(基本有二類: GET/POST ),將這些接收到的數據傳入 QUERY_STRING或 變量中, CGI 程序可以通過標準輸入,在程序中接收這些數據。當要向瀏覽器發送信息時,只要向 Web 服務器發送特定的文件頭信息,即可通過標準輸出將信息發往 Web 服務器, Web 服務器處理完這些由 CGI 程序發來的信息后就會將這些信息發送給瀏覽器。這樣就是 CGI 程序的通信方式了。
2 、接收數據
用 GET 方式接收到的數據保存在 Web 服務器的 QUERY_STRING 變量里,而通過 POST 方式接收到的數據是保存在 這個 Web 服務器變量里。它們的唯一區別就是:以 GET 方式接收的數據是有長度限制,而用 POST 方式接收的數據是沒有長度限制的。并且,以 GET 方式發送數據,可以通過 URL 的形式來發送,但 POST 方式發送的數據必須要通過 Form 才到發送。
好,現在讓我們用 C 語言寫一個神圣的 CGI 程序 -- Hello,World!
vi hello.c # 編輯源文件
// Begin
#include <stdio.h>
main() {
printf("Content-type:text/html\n\n");
printf("Hello,World!");
}
// End
gcc -o hello hello.c # 編譯
將該程序放在 Web 服務器的 cgi-bin 目錄下,然后通過以下方式訪問:
http://www.server.com/cgi-bin/examples/c/hello
這將在瀏覽器里打印出 Hello,World!
這 就算得上是一個 CGI 程序了,是不是很簡單? ^_^ 第一句 printf() 是打印頭信息,讓 Web 瀏覽器知道以下打印的數據是什么類型的數據,本例子中指定了 text/html 類型,即 html 文檔,所以下面的那句 printf() 打印的內容就會像我們寫網頁內容一樣在瀏覽器上顯示出來。
用 C/C++ 寫 CGI 的最難之處應算從瀏覽器接收數據!不過,借助現成的源程序,從瀏覽器接收數據也只不過是小菜一碟而已。
我說的這個現成的源程序是用 FireBird 的 bbs2www 程序包里提取出來的。在這里下載: cgi.c - cgi.h
經 本人提取出來的源程序只有兩個文件 cgi.c 和 cgi.h 。當要用它們來寫 CGI 程序時,只需在程序中加入 #include "cgi.c" 即可,現以例子說明一下使用方法。假設要通過 GET 方式從瀏覽器接收用戶的名字和 E-Mail 地址,源程序如下:
vi test.c # 編輯源文件
// Begin
#include <stdio.h>
#include "cgi.c"
main() {
char *name,*email;
cgi_init();
cgi_html_head();
name = cgi_get("name");
email = cgi_get("email");
printf("name = %s",name);
printf("<br>");
printf("email = %s",email);
cgi_quit();
}
// End
首 先定義兩個指針,然后調用 cgi_init() 來初始化 CGI 環境, cgi_html_head() 打印 HTML 文件類型信息,和 printf("Content-type:text/html\n\n"); 基本一樣,不過用 cgi_html_head() 打印的頭信息會指定文件的字符編碼為 gb2312 即中文字符。調用 cgi_get() 方法取得指定關鍵字 ( name 和 email ) 的值。當完成 CGI 部分的代碼后,要通用調用 cgi_quit() 和釋放 CGI 所點的系統資源。最后就像以住一樣去編譯程序, gcc -O6 -o test test.c
gcc -Wall test.c cgic.c -o test.cgi 然后將該程序放到 cgi-bin 目錄,接著通過以下方式調用該程序。
http://www.server.com/cgi-bin/examples/c/test?name=charles&email=charles@netease.com
cgic: 為 C 語言編寫 CGI 的 C 函數庫
由 Thomas Boutell 開發
目錄
CGIC 介紹
怎樣寫 CGIC 應用程序
怎樣產生圖片在 CGIC 中 ?
CGI 調試特征 : 利用捕獲
cgic 函數參考
cgic 變量參考
cgic 結果編碼參考
cgic 快速索引
一般的 UNIX 系統都支持 ANSIC, 增加相應的庫函數 ( 和相應的 h 文件 ) 就可以實現 CGI 。在此我向大家推薦一個用于 CGI 編程的 ANSIC 庫 :cgic 。
cgic 是用來生成基于 CGI 的 WWW 應用程序的 C 語言函數庫 , 它有以下功能 :
* 對數據進行語法分析
* 接收以 GET 和 PSOT 兩種方式發送的數據
* 把 FORM 中的不同域連接成連續的串
* 為檢索 FORM 數據而提供字符串 , 整數 , 浮點以及單項和多項選擇功能
* 為數字字段提供邊界檢測
* 把 CGI 環境變量加載到非空的 C 串中
* 為調試而捕捉 CGI 狀態
* 提供相對安全的系統調用功能
用一般 ANSI C 或 C++ 編譯器就可以編譯 cgic 程序 , 不過與通常 C 程序不同的是 , 用 cgic 寫的源碼其主函數是 cgiMain(), 而不是通常的 main() 。 cgic 的函數庫會自動把 cgiMain 連接到相應的 main() 上去。
--------------------------------------------------------------------------------
寫 CGIC 程序
Note: 所有的 cgic 應用程序必須連接 cgic.c.
用 cgimain() 替代 main() 必須包含: #include"cgic.h."
基本結構 cgictest.c:
int cgiMain() {
#if DEBUG
/* Load a saved CGI scenario if we're debugging */
cgiReadEnvironment("/path/to/capcgi.dat");
#endif
/* Important: we must indicate the type of document */
cgiHeaderContentType("text/html");
/* Now invoke other functions to handle each part of the form */
fprintf(cgiOut, "<HTML><HEAD>\n");
fprintf(cgiOut, "<T99vLE>cgic test</T99vLE></HEAD>\n"):
fprintf(cgiOut, "<BODY><H1>cgic test</H1>\n");
Name();
Address();
Hungry();
Temperature();
Frogs();
Color();
Flavors();
NonExButtons();
RadioButtons();
fprintf(cgiOut, "</BODY></HTML>\n");
/* This value will be the exit code of the program; 0
generally indicates success among Unix and DOS programs */
return 0;
}
capture
輸出標頭
cgiHeaderContentType() 在輸出文擋之前簡要說明 MIME 內型 , 如 "text/html" 。
cgiHeaderStatus() 代替輸出錯誤代碼 cgiHeaderLocation() 代替重新引導至其他頁面。在一個獨立的應用程序中只能有一個 cgiHeader 函數。
重點 : 在 cgiHeader 函數組中, cgiHeaderContentType(), 在任何向瀏覽器輸出之前被調用 . 否則將出錯或瀏覽器不能識別。 cgiOut
接著 , cgiMain() 調用不同的函數 . 當函數結束后 , 將返回 0
處理輸入文本
void Name() {
char name[81];
cgiFormStringNoNewlines("name", name, 81);
fprintf(cgiOut, "Name: %s<BR>\n", name);
}
這個函數的功能就是取的并顯示由用戶輸入的 name .
處理輸出
Important: cgiOut 通常相當于 stdout
cgiFormString 確保斷航
處理單一 Checkboxes 輸入
這個 Hungry() function 確定用戶是否選擇 "hungry" 這個 checkbox:
void Hungry() {
if (cgiFormCheckboxSingle("hungry") == cgiFormSuccess) {
fprintf(cgiOut, "I'm Hungry!<BR>\n");
} else {
fprintf(cgiOut, "I'm Not Hungry!<BR>\n");
}
}
這個函數依靠 cgiFormCheckboxSingle() 確定單一的 checkbox 被選擇。 cgiFormCheckboxSingle() 接受 checkbox 名字的屬性值,如果存在就返回 cgiFormSuccess ,否則返回 cgiFormNotFound 如果是多項 checkboxes ,就用 cgiFormCheckboxMultiple() 和 cgiFormStringMultiple() 函數 .
處理數字輸入
Temperature() 返回浮點書的值確保在特定的返回內。
void Temperature() {
double temperature;
cgiFormDoubleBounded("temperature", &temperature, 80.0, 120.0, 98.6);
fprintf(cgiOut, "My temperature is %f.<BR>\n", temperature);
}
依靠 cgiFormDoubleBounded() 得到數據 . 第一個數據是返回數據中輸入域的名字。最后一個值是用戶沒有提交時的默認值。
這個函數總是找回在特定返回內合適的值 ; cgiFormDoubleBounded 返回的值被檢查確信用戶輸入的資料在規定范圍內, 而不是其他無效的數據。查看 cgiFormDoubleBounded() 更多的資料 . 如果限度檢查不理想,可以用 cgiFormDouble() 替代 .
在整數輸入 ,cgiFormInteger 和 cgiFormIntegerBounded 可以利用 . 這些函數的功能類似 .
處理單一選擇輸入
<SELECT> HTML 標簽被用于向用戶提供幾個選擇 . Radio buttons 和 checkboxes 椰油這樣的用途,大門、能夠選擇的數量很小時 . Color()
char *colors[] = {
"Red",
"Green",
"Blue"
};
void Color() {
int colorChoice;
cgiFormSelectSingle("colors", colors, 3, &colorChoice, 0);
fprintf(cgiOut, "I am: %s<BR>\n", colors[colorChoice]);
}
這個函數確定用戶選擇了幾個選項從 <SELECT> 在表但的列表 . cgiFormSelectSingle()
cgiFormSelectSingle() 總是顯示合理的選項值 .
radio button 也可以用這個函數 . 另外還有 cgiFormRadio(), 也是一樣的
處理多項選擇的輸入
NonExButtons()
char *votes[] = {
"A",
"B",
"C",
"D"
};
void NonExButtons() {
int voteChoices[4];
int i;
int result;
int invalid;
char **responses;
/* Method #1: check for valid votes. This is a good idea,
since votes for nonexistent candidates should probably
be discounted... */
fprintf(cgiOut, "Votes (method 1):<BR>\n");
result = cgiFormCheckboxMultiple("vote", votes, 4,
voteChoices, &invalid);
if (result == cgiFormNotFound) {
fprintf(cgiOut, "I hate them all!<p>\n");
} else {
fprintf(cgiOut, "My preferred candidates are:\n");
fprintf(cgiOut, "<ul>\n");
for (i=0; (i < 4); i++) {
if (voteChoices[i]) {
fprintf(cgiOut, "<li>%s\n", votes[i]);
}
}
fprintf(cgiOut, "</ul>\n");
}
參考 cgiFormCheckboxMultiple(), cgiFormSelectMultiple().
cgiFormCheckboxMultiple() cgiFormCheckboxMultiple
NonExButtons() 函數在 cgictest.c:
/* Method #2: get all the names voted for and trust them.
This is good if the form will change more often
than the code and invented responses are not a danger
or can be checked in some other way. */
fprintf(cgiOut, "Votes (method 2):<BR>\n");
result = cgiFormStringMultiple("vote", &responses);
if (result == cgiFormNotFound) {
fprintf(cgiOut, "I hate them all!<p>\n");
} else {
int i = 0;
fprintf(cgiOut, "My preferred candidates are:\n");
fprintf(cgiOut, "<ul>\n");
while (responses[i]) {
fprintf(cgiOut, "<li>%s\n", responses[i]);
i++;
}
fprintf(cgiOut, "</ul>\n");
}
/* We must be sure to free the string array or a memory
leak will occur. Simply calling free() would free
the array but not the individual strings. The
function cgiStringArrayFree() does the job completely. */
cgiStringArrayFree(responses);
}
參考 cgiFormStringMultiple()
cgiFormStringMultiple()
/* An array of strings; each C string is an array of characters */
char **responses;
cgiFormStringMultiple("vote", &responses);
檢查 CGI 環境變量
將用到的變量 這里 ,
產生圖象
#include "cgic.h"
#include "gd.h"
char *colors[] = {
"red", "green", "blue"
};
#define colorsTotal 3
int cgiMain() {
int colorChosen;
gdImagePtr im;
int r, g, b;
/* Use gd to create an image */
im = gdImageCreate(64, 64);
r = gdImageColorAllocate(im, 255, 0, 0);
g = gdImageColorAllocate(im, 0, 255, 0);
b = gdImageColorAllocate(im, 0, 0, 255);
/* Now use cgic to find out what color the user requested */
cgiFormSelectSingle("color", 3, &colorChosen, 0);
/* Now fill with the desired color */
switch(colorChosen) {
case 0:
gdImageFill(im, 32, 32, r);
break;
case 1:
gdImageFill(im, 32, 32, g);
break;
case 2:
gdImageFill(im, 32, 32, b);
break;
}
/* Now output the image. Note the content type! */
cgiHeaderContentType("image/gif");
/* Send the image to cgiOut */
gdImageGif(im, cgiOut);
/* Free the gd image */
gdImageDestroy(im);
return 0;
}
為調試而捕捉CGI 狀態
cgic 函數參考
cgiFormResultType cgiFormString( char *name, char *result, int max)
用于從輸入域中copy 字符串。他將域名max-1 字節中的字符copy 到緩沖區result 。若域不存在,則copy 一個空串到result 緩沖區。在此函數中所有的新行由換行符代表。
cgiFormResultType cgiFormStringNoNewlines( char *name, char *result, int max)
它與cgiFormString 函數相似,只是所有的CR 和LF 都被去掉了。
cgiFormResultType cgiFormStringSpaceNeeded( char *name, int *length)
它返回指向name 的字符串的長度,并將長度放入length 中。
cgiFormResultType cgiFormStringMultiple( char *name, char ***ptrToStringArray)
若同一名字有多個輸入域,或域中的字符串可以動態變化,那么你可以使用本函數。它把名為name 的所有輸入域的值放在prtToStringArray 中。
void cgiStringArrayFree(char **stringArray)
它釋放了分配給stringArray 的內存。
cgiFormResultType cgiFormInteger( char *name, int *result, int defaultV)
從輸入域中取出整數放入result 中。
cgiFormResultType cgiFormIntegerBounded( char *name, int *result, int min, int max, int defaultV)
若輸入域中的整數在界限內則取出并放入result 中。
cgiFormResultType cgiFormDouble( char *name, double *result, double defaultV)
從輸入域中取出浮點數放入result 中。
cgiFormResultType cgiFormDoubleBounded( char *name, double *result, double min, double max, double defaultV)
若輸入域中的浮點數在界限內則取出并放入result 中。
cgiFormResultType cgiFormSelectSingle( char *name, char **choicesText, int choicesTotal, int *result, int defaultV)
取出復選框( 跟在select 語句之后的) ,把選擇的名字copy 到choicesText ,把選擇的個數copy 到choicesTotal ,把當前的選擇copy 到result 。
cgiFormResultType cgiFormSelectMultiple( char *name, char **choicesText, int choicesTotal, int *result, int *invalid)
與cgiFormSelectSingle 類似,只指向整型數組的result 代表了選擇的項。
cgiFormResultType cgiFormCheckboxSingle( char *name)
若復選框被選中,則函數返回cgiFormSuccess ,否則返回cgiFormNotFound 。
cgiFormResultType cgiFormCheckboxMultiple( char *name, char **valuesText, int valuesTotal, int *result, int *invalid)
與cgiFormCheckboxSingle 類似,但它處理同一名字有多個復選框的情況。name 指向復選框的名字;valuesText 指向包含有每 個復選框中參數的一個數組;valuesTotal 指向復選框的總數;result 是一個整型數組,每個復選框選中的用1 代表,沒選中的用0 代表。
cgiFormResultType cgiFormRadio( char *name, char **valuesText, int valuesTotal, int *result, int defaultV)
與cgiFormCheckboxMultiple 相似,只是這里是單選按鈕而不是復選框。
void cgiHeaderLocation(char *redirectUrl)
重定向到redirectUrl 指定的URL 。
void cgiHeaderStatus(int status, char *statusMessage)
輸出狀態代碼status 和消息statusMessage 。
void cgiHeaderContentType(char *mimeType)
用于告知瀏覽器返回的是什么類型的文檔。
cgiEnvironmentResultType cgiWriteEnvironment(char *filename)
本函數把當前CGI 環境寫入filename 文件中以便以后調試時使用
cgiEnvironmentResultType cgiReadEnvironment(char *filename)
本函數從filename 文件中讀取CGI 環境以便用來調試。
int cgiMain()
一個程序必須要寫這個函數, 這是主程序開始之處。
cgic 變量參考
This section provides a reference guide to the various global variables provided by cgic for the program mer to utilize. These variables should always be used in preference to stdin, stdout, and calls to getenv() in order to ensure compatibility with the cgic CGI debugging features.
大多數的變量相當于各種 CGI 變量,重要的是 VGIC 的變量不能為空 .
char *cgiServerSoftware
服務器軟件名稱 , 或者一個空的字符串 or to an empty string if unknown.
char *cgiServerName
返回服務器名稱或空
char *cgiGatewayInterface
網關接口 ( 通常是 CGI/1.1), 或空
char *cgiServerProtocol
網絡協議 (usually HTTP/1.0), 或空
char *cgiServerPort
服務器端口 (usually 80), 或空
char *cgiRequestMethod
請求方式 (usually GET or POST), 或空
char *cgiPathInfo
指出附加虛擬路徑
char *cgiPathTranslated
指出附加虛擬路徑并由服務器轉為本地路徑
char *cgiScriptName
調用程序的名字
char *cgiQueryString
包含 GET-method 請求或者 <ISINDEX> 標簽 . 這個信息不需要解吸,除非用 <ISINDEX> 標簽通常由 CGIC 函數庫自動解析。
char *cgiRemoteHost
從瀏覽器返回客戶主機的名字
char *cgiRemoteAddr
從瀏覽器返回客戶的 IP 地址
char *cgiAuthType
返回用戶授權信息
char *cgiRemoteUser
鑒別用戶 cgiAuthType.
char *cgiRemoteIdent
返回用戶的名字(用戶通過用戶堅定協議)這個消息是不安全的,特別是 Windows 系統。
char *cgiContentType
返回 MIME 內型
char *cgiAccept
參考 cgiHeaderContentType() cgiUserAgent
char *cgiUserAgent
取的用戶瀏覽器信息
char *cgiReferrer
指向用戶訪問的 URL.
int cgiContentLength
表單或查詢數據的字節被認為是標準的 .
FILE *cgiOut
CGI 輸出 . cgiHeader 函數 , 象 cgiHeaderContentType, 首先被用于輸出 mime 頭 ; 用于 fprintf() 和 fwrite(). cgiOut 通常相當于 stdout 。
FILE *cgiIn
CGI 輸入 . 在決大部分時間你都不會需要這個函數。
cgic 結果編碼參考
在大量的按列中 , cgic 函數有計劃的產生合理的結果,甚至瀏覽器和用戶不合理時。無論如何 , 有時候知道不合理的事情發生,尤其賦予一個值或定義一個范圍是一個不充分的解決方案。下面的這些結果編碼有助更好了解。
cgiFormSuccess
提交信息成功
cgiFormTruncated
刪除部分字節 .
cgiFormBadType
錯誤的輸入信息(沒有按要求)
cgiFormEmpty
提交信息為空 .
cgiFormNotFound
提交信息沒有找到 .
cgiFormConstrained
數字屬于某個特定的范圍,被迫低于或高于適當范圍。
cgiFormNoSuchChoice
單一選擇提交的值是不被接受。通常說明表但和程序之間存在矛盾。
cgiEnvironmentIO
從 CGI 環境或獲取的文件讀或寫的企圖失敗,報出 I/O 的錯誤。
cgiEnvironmentMemory
從 CGI 環境或獲取的文件讀或寫的企圖失敗,報出 out-of-memory 的錯誤。
cgiEnvironmentSuccess
從 CGI 環境或獲取的文件讀或寫的企圖成功。
cgic 快速索引
cgiAccept | cgiAuthType | cgiContentLength | cgiContentType | cgiEnvironmentIO | cgiEnvironmentMemory | cgiEnvironmentSuccess | cgiFormBadType | cgiFormCheckboxMultiple() | cgiFormCheckboxSingle() | cgiFormConstrained | cgiFormDouble() | cgiFormDoubleBounded() | cgiFormEmpty | cgiFormInteger() | cgiFormIntegerBounded() | cgiFormNoSuchChoice | cgiFormNotFound | cgiFormRadio() | cgiFormSelectMultiple() | cgiFormSelectSingle() | cgiFormString() | cgiFormStringMultiple() | cgiFormStringNoNewlines() | cgiFormStringSpaceNeeded() | cgiFormSuccess | cgiFormTruncated | cgiGatewayInterface | cgiHeaderContentType() | cgiHeaderLocation() | cgiHeaderStatus() | cgiIn | cgiMain() cgiOut | cgiPathInfo | cgiPathTranslated | cgiQueryString | cgiReadEnvironment() | cgiReferrer() | cgiRemoteAddr | cgiRemoteHost | cgiRemoteIdent | cgiRemoteUser | cgiRequestMethod | cgiScriptName | cgiServerName | cgiServerPort | cgiServerProtocol | cgiServerSoftware | cgiStringArrayFree() | cgiUserAgent | cgiWriteEnvironment()
CGIC 簡明教程
本系列的目的是演示如何使用C 語言的CGI 庫“CGIC” 完成Web 開發的各種要求。
基礎知識
* 1: 使用CGIC 的基本思路
* 2: 獲取Get 請求字符串
* 3: 反轉義
* 4: 獲取請求中的參數值
進階訓練
* 用CGIC 實現文件上傳
CGIC 簡明教程1 :使用CGIC 的基本思路
C 語言編程是一項復雜且容易出錯的工作,所以在完成復雜任務時,一定要選擇合適的庫。對于用C 語言編寫CGI 程序則更是如此。
CGIC 是非常優秀的C 語言CGI 庫函數。 其下載地址為:www.boutell.com/cgic/#obtain ,現在的版本號是2.05 。
本站從今天開始,將逐步介紹如何使用CGIC 完成各種操作,也可以說是一個Tutorial 。
(注:本系列涉及的編程環境都是Linux ,Windows 用戶需要對用到的操作系統命令稍作修改)
本文綱要 :
CGIC 的安裝、測試安裝、使用CGIC 的基本思路;
1) CGIC 的下載安裝
從上面提供的官方網址下載了CGIC 庫之后,解開壓縮包,里面有大約10 個文件,有用的是:
cgic.h :頭文件;
cgic.c :CGIC 的源代碼文件;
cgictest.c :CGIC 庫的作者提供的一個CGI 程序例子;
capture.c :用于調試CGI 程序的工具;
Makefile :安裝CGIC 的腳本文件;
可以看到,整個庫實際上就是cgic.c 一個文件,可以說是非常的精煉。
我們可以把CGIC 安裝為操作系統的一個動態鏈接庫,這樣我們每次編譯的時候,就不需要有cgic.c 這個源文件了。
但是由于需要(以后將會看到),我們將修改cgic.c 代碼,所以我們不把它安裝進系統。每次編譯的時候,只要把cgic.c 和cgic.h 放到當前文件夾就好了。
2) 測試安裝
在開始編寫你自己的CGI 程序之前,一定要先走通他的例子程序,免得后來程序出錯的時候還不知道是配置有問題,還是你的程序代碼有問題。
我們用他自帶cgictest.c 來實現自己的第一個C 語言CGI 程序。
你可以新建一個工作目錄,用于存放你的CGI 程序源代碼,把cgic.h, cgic.c, cgictest.c 三個文件拷貝到這個目錄,然后建立一個Makefile 文件,其內容為:
1. test.cgi:cgictest.c cgic.h cgic.c
2. gcc -wall cgictest.c cgic.c -o test.cgi
需要提醒的是,第二行開頭一定是一個tab 鍵(且僅有一個),不能使用空格。
保存好Makefile 的內容之后,執行make 命令:
make
我們看到,當前目錄下應該多了一個test.cgi 文件。
在 你的網站根目錄下建立一個cgi-bin 目錄(當然名字可以任意取,但作為習慣,一般叫做cgi-bin ),然后在Apache 的配置文件里賦予其執行 CGI代碼的權限,權限修改完之后要重啟Apache 。完成之后,把剛才生成的test.cgi 放到cgi-bin 目錄中。此時我們可以在瀏覽器中輸入以 下地址進行訪問:
http://127.0.0.1/cgi-bin/test.cgi
如果正常的話,應該看到一個網頁被展示出來。這樣,第一個C 語言的CGI 程序就運行起來了。
如果瀏覽器報錯,那么多半是配置Apache 的時候有些操作沒有正確完成。
3) 使用CGIC 的基本思路
從 cgic.c 的代碼可以看出,它定義了main 函數,而在cgictest.c 中定義了一個cgiMain 函數。也就是說,對于使用CGIC 編寫的CGI 程序,都是從cgic.c 中的代碼進入,在庫函數完成了一系列必要的操作(比如解析參數、獲取系統環境變量)之后,它才會調用你的代碼(從你定義的 cgiMain 進入)。
另外一點就是,cgi 程序輸出HTML 頁面的方式都是使用printf 把頁面一行一行地打印出來,比如cgictest.c 中的這一段代碼:
fprintf(cgiOut, "<textarea NAME=\"address\" ROWS=4 COLS=40>\n");
fprintf(cgiOut, "Default contents go here. \n");
fprintf(cgiOut, "</textarea>\n");
上面這段代碼的運行結果就是在頁面上輸出一個textarea 。 第一個參數cgiOut 實際上就是stdin ,所以我們可以直接使用printf ,而不必使用fprintf 。不過在調試的時候會用到fprintf 來重定向輸出。
這種方式與Java Servlet 非常類似,Servlet 也是通過調用打印語句System.out.println(…) 來輸出一個頁面。(不過后來Java 推出了JSP 來克服這種不便。)
但是與Servlet 不同的地方在于,使用C 語言的我們還要自己輸出HTML 頭部(聲明文檔類型):
cgiHeaderContentType("text/html");
這個語句的調用一定要在所有printf 語句之前。而這個語句執行的任務實際上就是:
void cgiHeaderContentType(char *mimeType) {
fprintf(cgiOut, "Content-type: %s\r\n\r\n", mimeType);
}
這個語句告訴瀏覽器,這次傳來的數據是什么類型,是一個HTML 文檔,還是一個bin 文件… 如果是個HTML 文檔,就通過瀏覽器窗口顯示,如果是一個bin (二進制)文件,則打開下載窗口,讓用戶選擇是否保存文件以及保存文件的路徑。
理解了這幾點之后,你就可以編寫自己的CGIC 程序了。新建一個文件test.c 試試:
下載: test.c
1. #include <stdio.h>
2. #include "cgic.h"
3. #include <string.h>
4. #include <stdlib.h>
5. int cgiMain() {
6. cgiHeaderContentType("text/html");
7. fprintf(cgiOut, "<HTML><HEAD>\n");
8. fprintf(cgiOut, "<TITLE>My First CGI</TITLE></HEAD>\n");
9. fprintf(cgiOut, "<BODY><H1>Hello CGIC</H1></BODY>\n");
10. fprintf(cgiOut, "</HTML>\n");
11. return 0;
12. }
把Makefile 文件中的cgitest.c 全部換稱test.c ,保存,再執行make 命令即可。
此時通過瀏覽器訪問,會在頁面上看到一個大大的“Hello CGIC” 。
CGIC 簡明教程2 :獲取Get 請求字符串
Get 請求就是我們在瀏覽器地址欄輸入URL 時發送請求的方式,或者我們在HTML 中定義一個表單(form )時,把action 屬性設為“Get” 時的工作方式;
Get 請求字符串就是跟在URL 后面以問號“?” 開始的字符串,但不包括問號。比如這樣的一個請求:
http://127.0.0.1/cgi-bin/out.cgi?ThisIsTheGetString
在上面這個URL 中,“ThisIsTheGetString” 就是Get 請求字符串。
在進入我們自己編寫的cgi 代碼之前,CGIC 庫已經事先把這個字符串取到了,我們可以在程序中直接獲得,要做的僅僅是在你編寫的cgiMain 方法前面加入以下聲明:
extern char *cgiQueryString;
現在給出一個簡單的例子,這個例子跟上一篇的測試程序非常相似,只不過程序的輸出是使用者輸入的Get 請求字符串。
下載: test.c
1. #include <stdio.h>
2. #include "cgic.h"
3. #include <string.h>
4. #include <stdlib.h>
5.
6. extern char *cgiQueryString;
7. int cgiMain() {
8. cgiHeaderContentType("text/html");
9. fprintf(cgiOut, "<HTML><HEAD>\n");
10. fprintf(cgiOut, "<TITLE>My CGIC</TITLE></HEAD>\n");
11. fprintf(cgiOut, "<BODY>");
12. fprintf(cgiOut, "<H1>%s</H1>",cgiQueryString);
13. fprintf(cgiOut, "</BODY>\n");
14. fprintf(cgiOut, "</HTML>\n");
15. return 0;
16. }
假設把這個程序編譯成out.cgi (編譯方法參見上一篇),并部署到Web 服務器的cgi-bin 目錄下,當用戶在瀏覽器地址欄輸入本文開頭給出的URL 字符串時,瀏覽器頁面上會顯示:
ThisIsTheGetString
我們也可以編寫一個用于測試的HTML 頁面:
下載: test.html
1. <html>
2. <head>
3. <title>Test</title>
4. </head>
5. <body>
6. <form action="cgi-bin/out.cgi" method="get">
7. <input type="text" name="theText">
8. <input type="submit" value="Continue →">
9. </form>
10. </body>
11. </html>
文件的部署結構應該為:
|test.html
|—-cgi-bin/out.cgi
大家可以試試,通過瀏覽器訪問http://127.0.0.1/test.html ,在文本框內輸入一些字符,并點擊提交按鈕,然后就可以看到cgi 程序的執行結果:把在文本框輸入的字符原樣顯示在瀏覽器上。
CGIC 簡明教程3 :反轉義
瀏覽器在發送Get 請求時,會把請求字符串進行轉義操作(英文術語為: escape ); 比如,我們在地址欄輸入(注意最后”it’s me” 中的空格):
http://localhost/~Jack/cgi-bin/out.cgi?it's me
瀏覽器會把它轉義為:
http://localhost/~Jack/cgi-bin/out.cgi?it's%20me
在上一篇最后給出的例子中,如果在文本框內輸入
it's me
你會發現,瀏覽器最終發送的請求為
http://localhost/~Jack/cgi-bin/out.cgi?theText=it%27s+me
通過CGIC ,我們可以把這些被轉義后的字符還原為我們本來的輸入,這個過程就叫“ 反轉義” (Unescape) 。
不過這個過程有點像hack 他的代碼。
整個過程分三個步驟:
1 )打開cgic.c ,找到這一行語句:
static cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
注意,我們要找的只是這個函數聲明,不是函數定義;
2 )在這個函數聲明語句的上方,你會看到一個結構體定義:
1. typedef enum {
2. cgiUnescapeSuccess,
3. cgiUnescapeMemory
4. } cgiUnescapeResultType;
把這幾行語句復制到cgic.h 文件中,并在這里把它注釋掉;
同時還要刪除在第一步中找到的函數聲明語句中的“static” 關鍵字。
3 )我們現在就可以使用反轉義函數cgiUnescapeChars 了:
在你自己的代碼(按照慣例,還是test.c )中,加入以下聲明語句即可
extern cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
接下來我們給出一段完整的test.c 代碼
下載: test.c
1. #include <stdio.h>
2. #include "cgic.h"
3. #include <string.h>
4. #include <stdlib.h>
5.
6. extern char *cgiQueryString;
7. extern cgiUnescapeResultType cgiUnescapeChars(char **sp, char *cp, int len);
8. int cgiMain() {
9. char * buffer;
10. cgiHeaderContentType("text/html");
11. fprintf(cgiOut, "<HTML><HEAD>\n");
12. fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n");
13. fprintf(cgiOut, "<BODY>");
14. cgiUnescapeChars(&buffer, cgiQueryString, strlen(cgiQueryString));
15. fprintf(cgiOut, "<H1>%s</H1>",buffer);
16. fprintf(cgiOut, "</BODY>\n");
17. fprintf(cgiOut, "</HTML>\n");
18. free(buffer);
19. return 0;
20. }
值得注意的是,buffer 的存儲空間是cgiUnescapeChars 幫你分配的,但最后要由你自己來釋放(free ),這一點千萬不可忘記。
下面你可以結合上一篇給出的測試用html 代碼試試該cgi 程序的運行結果,也可以直接在瀏覽器地址欄輸入一些帶有特殊符號的字符串。
最后講一下為什么不得不用這種hacker 的方式來完成該任務,而CGIC 不顯式提供?
CGIC 的出發點是,我們平時只需要解析請求中的鍵值對,比如:”?q=nice&client=IE” ,當我們在服務端查詢“q” 的值時,我們就能得到 “nice” 。CGIC 有一族函數幫助我們完成這個任務,比如cgiFormString (以后會講到)。在解析這種請求格式的時候,如果我們提供的參數 值含有被轉義的字符,那么CGIC 就會在內部調用cgiUnescapeChars 完成反轉義。
但是,有時候我們會發送非常復雜的Get 請求字符串,但并不是“ 鍵-值” 對的格式。這就需要直接使用cgiUnescapeChars 進行反轉義了。
例如:假設我們有個服務端cgi 程序chat.cgi ,這是個網絡聊天機器人(也許你可以開發自己的Web 版MSN 機器人、QQ 機器人)。如果我們發送如下請求:
http://127.0.0.1/cgi-bin/chat.cgi?"this is a cgi user"
那么chat.cgi 就會把“this is a cgi user” 當做你對它說的話,經過處理,它會回復一段語句。為了方便,我們并沒有寫成“ 鍵-值” 對的形式。這個時候被我們hack 的cgiUnescapeChars 就能派上用場了。
CGIC 簡明教程4 :獲取請求中的參數值
我們在提交一個表單(form) 時,怎樣把表單內的值提取出來呢?
比如下面這個表單:
<form action="cgi-bin/out.cgi" method="POST">
<input type="text" name="name" />
<input type="text" name="number" />
<input type="submit" value="Submit" />
</form>
當out.cgi 收到請求時,需要把輸入框”name” 和輸入框”number” 內的值提取出來。而且不管form 中的action 是GET 還是POST ,都要有效。
下面給出示例代碼:
下載: test.c
1. #include <stdio.h>
2. #include "cgic.h"
3. #include <string.h>
4. #include <stdlib.h>
5.
6. int cgiMain() {
7. char name[241];
8. char number[241];
9. cgiHeaderContentType("text/html");
10. fprintf(cgiOut, "<HTML><HEAD>\n");
11. fprintf(cgiOut, "<TITLE>My CGI</TITLE></HEAD>\n");
12. fprintf(cgiOut, "<BODY>");
13. cgiFormString("name", name, 241);
14. cgiFormString("number", number, 241);
15. fprintf(cgiOut, "<H1>%s</H1>",name);
16. fprintf(cgiOut, "<H1>%s</H1>",number);
17. fprintf(cgiOut, "</BODY>\n");
18. fprintf(cgiOut, "</HTML>\n");
19. return 0;
20. }
從上面的代碼可以看出,第13 行和第14 行獲取了輸入框的值。
獲取輸入參數值在CGIC 中其實有一族函數,cgiFormString 是其中最常用的一個。
cgiFormStringNoNewlines 用來去掉換行符(如果用戶是在一個TextArea 里輸入字符的話);
cgiFormStringSpaceNeeded 用于測試輸入值的長度,可以以此為依據,然后按需精確分配緩沖區。
用C 語言庫(CGIC) 編寫CGI ,實現文件上傳
用C 語言編寫cgi 程序的話,多半會用到CGIC 。 這是個非常流行的庫,遇到文件上傳之類的應用更是離不開它。官方頁面及下載地址為:www.boutell.com/cgic/#obtain
不少網站都有文件上傳的功能,本文展示如何用CGIC 庫編寫文件上傳的服務端程序,最后給出一段簡單的HTML 代碼,供大家測試使用 。
下載: upload.c
1. #include<stdio.h>
2. #include<string.h>
3. #include<unistd.h>
4. #include<fcntl.h>
5. #include<sys/stat.h>
6. #include"cgic.h"
7. #define BufferLen 1024
8. int cgiMain(void){
9. cgiFilePtr file;
10. int targetFile;
11. mode_t mode;
12. char name[128];
13. char fileNameOnServer[64];
14. char contentType[1024];
15. char buffer[BufferLen];
16. char *tmpStr=NULL;
17. int size;
18. int got,t;
19. cgiHeaderContentType("text/html");
20. // 取得html 頁面中file 元素的值,應該是文件在客戶機上的路徑名
21. if (cgiFormFileName("file", name, sizeof(name)) !=cgiFormSuccess) {
22. fprintf(stderr,"could not retrieve filename\n");
23. goto FAIL;
24. }
25. cgiFormFileSize("file", &size);
26. // 取得文件類型,不過本例中并未使用
27. cgiFormFileContentType("file", contentType, sizeof(contentType));
28. // 目前文件存在于系統臨時文件夾中,通常為/tmp ,通過該命令打開臨時文件。臨時文件的名字與用戶文件的名字不同,所以不能通過路徑/tmp/userfilename 的方式獲得文件
29. if (cgiFormFileOpen("file", &file) != cgiFormSuccess) {
30. fprintf(stderr,"could not open the file\n");
31. goto FAIL;
32. }
33. t=-1;
34. // 從路徑名解析出用戶文件名
35. while(1){
36. tmpStr=strstr(name+t+1,"\\");
37. if(NULL==tmpStr)
38. tmpStr=strstr(name+t+1,"/");//if "\\" is not path separator, try "/"
39. if(NULL!=tmpStr)
40. t=(int)(tmpStr-name);
41. else
42. break;
43. }
44. strcpy(fileNameOnServer,name+t+1);
45. mode=S_IRWXU|S_IRGRP|S_IROTH;
46. // 在當前目錄下建立新的文件,第一個參數實際上是路徑名,此處的含義是在cgi 程序所在的目錄(當前目錄))建立新文件
47. targetFile=open(fileNameOnServer,O_RDWR|O_CREAT|O_TRUNC|O_APPEND,mode);
48. if(targetFile<0){
49. fprintf(stderr,"could not create the new file,%s\n",fileNameOnServer);
50. goto FAIL;
51. }
52. // 從系統臨時文件中讀出文件內容,并放到剛創建的目標文件中
53. while (cgiFormFileRead(file, buffer, BufferLen, &got) ==cgiFormSuccess){
54. if(got>0)
55. write(targetFile,buffer,got);
56. }
57. cgiFormFileClose(file);
58. close(targetFile);
59. goto END;
60. FAIL:
61. fprintf(stderr,"Failed to upload");
62. return 1;
63. END:
64. printf("File \"%s\" has been uploaded",fileNameOnServer);
65. return 0;
66. }
假設該文件存儲為upload.c ,則使用如下命令編輯:
gcc -Wall upload.c cgic.c -o upload.cgi
編譯完成后把upload.cgi 復制到你部署cgi 程序的目錄(通常命名為cgi-bin )。
正式部署時,請務必修改用open 創建新文件那一行代碼。把open 的第一個參數設置為目標文件在服務器上存儲的絕對路徑,或者相對于cgi 程序的相對路徑。本例中,出于簡單考慮,在cgi 程序所在目錄下創建新文件。
測試用HTML 代碼:
下載: upload.html
1. <form target="_blank" method="post" action="cgi-bin/upload.cgi">
2. <input name="file" type="file" /> <input name="submit" type="submit" />
3. </form>
最后的文件目錄結構為
/MyWebRoot
|—/upload.html
|—/cgi-bin
|——/upload.cgi
當然,你必須配置能夠cgi-bin ,并且程序要有權限在cgi-bin 目錄下創建文件(因為此例把文件上傳到cgi-bin 目錄下)。
那么如何控制上傳文件的大小呢?因為你有時會不允許用戶上傳太大的文件。
通過分析cgic.c 的源代碼,我們發現它定義了一個變量cgiContentLength ,表示請求的長度。但我們需要首先判斷這是一個上傳文件的請求,然后才能根據cgiContentLength 來檢查用戶是否要上傳一個太大的文件。
cgic.c 的main 函數中進行了一系列if-else 判斷來檢查請求的類型,首先確定這是一個post 請求,然后確定數據的編碼方式為 “multipart/form-data” ,這個判斷通過之后,就要開始準備接收數據了。所以我們要在接收數據開始之前使用 cgiContentLength 判斷大小,如果超過標準,就立即返回,不允許繼續操作。
下面貼出修改后代碼片段(直接修改cgic.c 的源代碼即可):
1. else if (cgiStrEqNc(cgiContentType, "multipart/form-data")) {
2. #ifdef CGICDEBUG
3. CGICDEBUGSTART
4. fprintf(dout, "Calling PostMultipartInput\n");
5. CGICDEBUGEND
6. #endif /* CGICDEBUG */
7. // 我的代碼
8. //UpSize: 文件長度上限值,以byte 為單位,UpSize 是一個int 變量,因為cgiContentLength 的類型為int
9. if(cgiContentLength>UpSize){
10. cgiHeaderContentType("text/html");
11. printf("File too large!\n");
12. cgiFreeResources();
13. return -1;
14. }
15. // 我的代碼結束
16. if (cgiParsePostMultipartInput() != cgiParseSuccess) {
17. #ifdef CGICDEBUG
18. CGICDEBUGSTART
19. fprintf(dout, "PostMultipartInput failed\n");
20. CGICDEBUGEND
21. #endif /* CGICDEBUG */
22. cgiFreeResources();
23. return -1;
24. }
25. #ifdef CGICDEBUG
26. CGICDEBUGSTART
27. fprintf(dout, "PostMultipartInput succeeded\n");
28. CGICDEBUGEND
29. #endif /* CGICDEBUG */
30. }
31. }
變量UpSize 表示文件大小的上限。在cgic.c 的main 中找到相關代碼,并修改成上面這樣即可。你可以在cgic.c 中定義UpSize ,也可以在剛才完成的upload.c 中定義,然后在cgic.c 中用extern 方式引用。
原文鏈接 : http://blog.csdn.net/hurtmanzc/archive/2007/08/24/1757747.aspx
RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成