進公司不就,剛完成了公司交給的第一個跟嵌入式有關系的產品開發工作。本人Java Web出身,具有傳奇的求職經驗(不為吹噓,請大家不要為這句話糾纏不清),竟然稀裡糊塗的就從一個做Java Web的,華麗的轉身成為一個做跟我夢寐以求的嵌入式的人!廢話太多不好,先總結一下這個產品開發中遇到的問題,供以後參考。
公司是一個轉型公司,最近幾年才開始注重研發的。公司裡面除了我是Java Web出身,其他技術研發的都是C的,剛進來鴨梨比較大,雖然自己有一定的C和C++基礎。
剛進來就是對公司產品的熟悉,讀了一下源碼(linux下,基本上都是C),感覺比較吃力,沒過多久,老大就讓我對公司一個產品的配置界面的進行改造,這個是需要直接放進flash中的,工程容量的最大允許量1M!由於剛開始沒有進行深入的研究,一直秉承著做java web的思維,認為工程大小無所謂,反正是放在電腦上的,硬盤不夠就擴容就行了。但是後來發現,嵌入式的產品對這個很敏感,在作設計的時候就應該考慮如何去給程序瘦身,如何去提高代碼質量。
花了差不多2個月的時間,將產品的改造初步完成,接下來就是一些很小的修修補補的事了,老大把剩下的事交給了一直跟我協作的那個人,讓我開始投入到公司PBX相關的研究與開發中。
公司PBX的東西,屬於核心產品,我要做的第一件事就是將原有的配置界面的風格跟之前做的一個產品進行統一,(cgi(linux下c實現) + jquery)。又一次被打擊:linux下開發,不停的vi,還有不得不去了解熟悉的lighttpd和cgi(c實現)的通信原理和深層次應用。之前大致對這個產品進行了分心,估計是要經歷一次很痛苦的開發。。。
不過,作IT的,注定了需要不斷去學習。下面將我對cgi的一些了解寫下來,歡迎同行們能拍磚。
[color=olive]一,CGI概述
CGI(公共網關接口)規定了Web服務器調用其他可執行程序(CGI)的接口協議標准。Web服務器通過調用CGI程序實現和Web浏覽器的交互,也就是CGI程序接收Web浏覽器發送給Web服務器的信息進行處理,將相應結果送給Web服務器及Web浏覽器。CGI程序一般完成Web網頁中表單數據的處理,數據庫查詢和實現與傳統應用系統的集成等工作。CGI程序可以用任何程序設計語言編寫,如shell、Perl、Fortran、Pascal、C等。但是用C語言編寫的CGI程序具有執行快、安全性高(因為C語言程序是編譯執行且不可被修改)等特點。
CGI接口標准包括標准輸入、環境變量、標准輸出三部分。
1. 標准輸入
CGI程序像其他可執行程序一樣,可通過標准輸入(stdin)從Web服務器得到輸入信息,如Form中的數據,這就是萎縮的向CGI程序傳遞數據的POST方法。這意味著在操作系統命令行狀態可執行CGI程序,並對CGI程序進行調試。(從開始接觸嵌入式開始,你就該適應頻繁跟操作系統,特別是unix和linux這樣的東西打交道的環境)。POST方法是常用的方法,下面我將以此方法為例,分析CGI程序設計的方法、過程和技巧。
2. 環境變量
操作系統提供了許多環境變量,它們定義了程序的執行環境,應用程序可以存取它們。Web服務器和CGI接口又另外設置了一些環境變量,用來向CGI程序傳遞一些重要的參數。CGI的GET方法還通過環境變量QUERY-STRING向CGI程序傳遞Form中的數據。
3. 標准輸出
CGI程序的標准輸出(stdout)將輸出信息傳送給Web服務器。傳送給Web服務器的信息可以用各種格式,通常以純文本或者html文本的形式,這樣我們就可以在命令行狀態調試CGI程序,並且得到它們的輸出。
下面是一個簡單的C實現的CGI程序,它將html中form的信息直接輸出到Web浏覽器。
C代碼
#include <stdin.h>
#include <stdlib.h>
main()
{
int i, n;
printf("Contenttype:text/plain\n\n");
n = 0;
if(getenv("CONTENT-LENGTH"))
n = atoi(getenv("CONTENT-LENGTH"));
for(i = 0;i < n;i++)
putchar(getchar());
putchar("\n");
fflush(stdout);
}
下面對程序作以下簡要的分析。
printf("Contenttype:text/plain\n\n");
此行通過標准輸出將字符串Contenttype:text/plain\n\n傳送給Web服務器。它是一個MINE頭信息,它告訴Web服務器隨後的輸出是以純ASCII文本的形式。注意:這個頭信息中有兩個換行符,這是因為Web服務器需要在實際的文本信息開始之前先看見一個空行。
if(getenv("CONTENT-LENGTH")) n = atoi(getenv("CONTENT-LENGTH"));
此行首先檢查CONTENT-LENGTH環境變量是否存在。Web服務器在調用使用POST方法的CGI程序時設置此環境變量,它的文本值表示Web服務器傳送給CGI程序的輸入中的字符數目,因此使用atoi()函數將此環境變量的值轉換成整數,並賦值給n。請注意,Web服務器並不以文件結束符來終止它的輸出,所以如果不檢查環境變量CONTENT-LENGTH,CGI程序就無法知道什麼時候輸入結束了。
for(i = 0;i < n;i++) putchar(getchar());
此行從0循環到CONTENT-LENGTH - 1次將標准輸入中讀到的每一個字符直接拷貝到標准輸出,也就是將所有的輸入以ASCII的形式送回給Web服務器。
通過此例,可將CGI程序的一般工作過程總結為如下幾點:
1. 通過檢查環境變量CONTENT-LENGTH確定有多少輸入
2. 循環使用getchar()或者其他文件讀函數得到所有的輸入
3. 以相應的方法處理輸入
4. 通過Contenttype頭信息,將輸出信息的格式告訴給Web服務器
5. 通過使用printf()或者putchar()或者其他文件寫函數,將輸出傳送給Web服務器。
二,環境變量
環境變量是文本串(名字/值對),可以別OS Shell或者其他程序設置,也可以被其他程序訪問。它們是Web服務器傳遞數據給CGI程序的簡單手段,之所以稱為環境變量是因為它們是全局變量,任何程序都可以存取它們。
下面是CGI程序設計中經常要用到的一些環境變量:
HTTP-REFERER:調用該CGI程序的網頁的URL
REMOTE-HOST:調用該CGI程序的Web浏覽器的機器名和域名
REQUEST-METHOD:當Web服務器傳遞數據給CGI程序時所采用的方法,分為GET和POST兩種。GET方法僅通過環境變量(如QUERY-STRING)傳遞數據給CGI程序,而POST方法通過環境變量和標准輸入傳遞數據給CGI程序,因此POST方法可較方便的傳遞數據給CGI程序。
SCRIPT—NAME:該CGI程序的名稱
QUERY-STRING:當使用POST方法時,Form中的數據最後放在QUERY-STRING中,傳遞給CGI程序
CONTENT-TYPE:傳遞給CGI程序數據的MIME類型,通常為"application/x-www-form-url-encoded",它是html form中以POST方法傳遞數據給CGI程序的數據編碼類型,成為URL編碼類型。
CONTENT-LENGTH: 傳遞給CGI程序的數據字符數(字節數)
三,Form中輸入的分析和解碼
1. 分析名字/值對
當用戶提交一個html form時,Web浏覽器首先對form中的數據以名字/值對的形式進行編碼,並發送給Web服務器,然後由Web服務器傳遞給CGI程序。其格式如下:
name1=value1&name2=value2&name3=value3&name4=value4...
其中名字是form中定義的input、select或textarea等tag的名字,值是用戶輸入或者選擇的值。這種格式即為URL編碼,程序中需要對其進行分析和解碼。要分析這種數據流,CGI程序必須首先將數據流分解成一組組的名字/值對,這可以通過在輸入流中查找下面的兩個字符來完成。
每當找到字符=,標志著一個form變量名字的結束;每當找到字符&,標志著一個form變量值的結束。清注意輸入數據的最後一個變量值不以&結束。
一旦名字/值對分解後,還必須將輸入中的一些特殊字符轉換成相應的ASCII字符,這些特殊字符是:
+ : 將+轉換成空格符
%xx : 用其十六進制ASCII碼值表示的特殊字符。根據值xx將其轉換成相應的ASCII字符。
對form變量名和變量值都要進行這種轉換。下面是一個對form數據進行分析並將結果回送給Web服務器的CGI程序。
C代碼
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
int htoi(char *);
main()
{
int i, n;
char c;
printf("Contenttype:text/plain\n\n");
n = 0;
if(getenv("CONTENT-LENGTH"))
n = atoi("CONTENT-LENGTH");
for(i = 0;i < n;i++)
{
int is-eq = 0;
c = getchar();
switch(c)
{
case '&' :
c = '\n';
break;
case '+' :
c = ' ';
break;
case '%' :
{
char s[3];
s[0] = getchar();
s[1] = getchar();
s[2] = 0;
c = htoi(s);
i += 2;
}
break;
case '=' :
c = ':';
is-eq = 1;
break;
};
putchar(c);
if(is-eq)
putchar(' ');
}
putchar('\n');
fflush(stdout);
}
/** Convert hex string to int **/
int htoi(char *s)
{
char *digits = "0123456789ABCDEF";
if(islower(s[0])) s[0] = toupper(s[0]);
if(islower(s[1])) s[1] = toupper(s[1]);
return 16 * (strchar(digits,s[0]) - strchar(digits, '0')) + (strchar(digits,s[1] - strchar(digits, '0')));
}
上面的程序首先輸出一個MIME頭信息給Web服務器,檢查輸入中的字符數,並循環檢查每個字符。
三,產生HTML輸出
CGI的輸出由兩部分組成:MIME頭信息和實際的信息。兩部分之間以一個空行分開。可以使用MIME頭信息為"Contenttype:text/html\n\n"來輸出html源代碼給Web服務器,下面是一個簡單的例子:
C代碼
#include <stdio.h>
#include <string.h>
main()
{
printf("Contenttype:text/html\n\n");
printf("<html>\n");
printf("<head><title>An HTML Page From a CGI</title></h ead>\n″");
printf("<h2> This is an HTML page generated from with i n a CGI program.. .</h2>\n");
printf("<hr><p>\n");
printf("<a href="../output.html#two"><b> Go back to out put.html page <
/b></a>\n");
printf("</body>\n");
printf("</html>\n");
fflush(stdout);
}
作者“telyy123”