芯靈思Sinlinx A33開發板boa與CGI移植
開發平臺
* 芯靈思SinlinxA33開發板
淘寶店鋪: https://sinlinx.taobao.com/
嵌入式linux 開發板交流 641395230
在嵌入式設備的管理與交互中,基於Web方式的應用成為目前的主流,這種程序結構也就是大家非常熟悉的B/S結構,即在 嵌入式設備上運行一個支持腳本或CGI功能的Web服務器,
能夠生成動態頁面,在用戶端只需要通過Web瀏覽器就可以對嵌入式設備進行管理和監控,非常方 便實用。本節主要介紹這種應用的開發和移植工作。用戶首先需要在嵌入式設備上成功移植支持腳本或CGI功能的Web服務器,然後才能進行應用程序的開發。
1、 嵌入式Web服務器移植 由於嵌入式設備資源一般都比較有限,並且也不需要能同時處理很多用戶的請求,因此不會使用Linux下最常用的如Apache 等服務器,而需要使用一些專門為嵌入式設備設計的Web服務器,
它們和Apache等高性能的Web服務器主要的區別在於它們一般是 單進程服務器,只有在完成一個用戶請求後才能響應
另一個用戶的請求,而無法並發響應,但這在嵌入式設備的應用場合裏已經足夠了。
我們紹比較常用的Boa服務器的移植。
Boa是一個非常小巧的Web服務器,可執行代碼只有約60KB。它是一個單任務Web服務器,只能依次完成用戶的請求,
而不會fork出新的進程來處理 並發連接請求。但Boa支持CGI,能夠為CGI程序fork出一個進程來執行。Boa的設計目標
第一步完成Boa程序的移植。從www.boa.org下載Boa源碼,當前最新版本為0.94.13,將其解壓並進入源碼目錄的src
子目錄
# tar xzf boa-0.94.13.tar.gz
# cd boa-0.94.13/src
生成Makefile文件
# ./configure
修改Makefile文件,找到CC=gcc,將其改成CC = arm-none-linux-gnueabi-gcc,再找到CPP = gcc –E,
將其改成CPP = arm-none-linux-gnueabi-gcc –E,並保存退出。
# make
# arm-none-linux-gnueabi-strip boa
第二步完成Boa的配置,使其能夠支持CGI程序的執行。Boa需要在/etc目錄下建立一個boa目錄,裏面放入Boa的主要
配置文件boa.conf。在Boa源碼目錄下已有一個示例boa.conf,可以在其基礎上進行修改,下面解釋一下該文件的含義:
監聽的端口號,缺省都是80,一般無需修改
Port 80
bind調用的IP地址,一般註釋掉,表明綁定到INADDR_ANY,通配於服務器的所有IP地址
Listen 192.68.0.5
作為哪個用戶運行,即它擁有該用戶的權限,一般都是nobody,需要/etc/passwd中有
nobody用戶
User nobody
作為哪個用戶組運行,即它擁有該用戶組的權限,一般都是nogroup,需要在/etc/group文
件中有nogroup組
Group nogroup
當服務器發生問題時發送報警的email地址,目前未用,註釋掉
ServerAdmin root@localhost
錯誤日誌文件。如果沒有以/開始,則表示從服務器的根路徑開始。如果不需要錯誤日誌,
則用#/dev/null。在下面設置時,註意一定要建立/var/log/boa目錄
ErrorLog /var/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/ /var/www/cgi-bin/
用戶可以根據自己需要,對boa.conf進行修改,但必須要保證其他的輔助文件和設置必須和boa.conf裏的配置相符,
不然Boa就不能正常工作。
/**************************************************************************************************/
一、CGIC簡介
1、CGI簡介
CGI(Common Gateway Interface)是外部應用擴展應用程序與WWW服務器交互的一個標準接口。按照CGI標準編寫的外部擴展應用程序可以處理客戶端瀏覽器輸入的數據,從而完成客戶端與服務器的交互操作。而CGI規範就定義了Web服務器如何向擴展應用程序發送消息,在收到擴展應用程序的信息後又如何進行處理等內容。通 過CGI可以提供許多靜態的HTML網頁無法實現的功能,比如搜索引擎、基於Web的數據庫訪問等等。
CGI的主要功能:
A、分析數據,並自動校正一些有缺陷的瀏覽器發來的數據
B、透明接收用GET或 POST方法發來的From數據
C、能接受上傳文件
D 、能夠設置和接收cookies
E、用一致的方式處理From元素裏的回車
F、提供字符串,整數,浮點數,單選或多選功能來接收數據
G、提供數字字段的邊界檢查
H、能夠將CGI環境變量轉化成C中的非空字符串
I、提供CGI程序的調試手段,能夠回放CGI程序執行時的CGI狀態
2、BOA與CGI工作機制
BOA和CGI的工作機制:
HTTP協議是WWW的基礎,基於客戶/服務器模型,服務器可以為分布在網絡中的客戶提供服務。HTTP是建立在TCP/IP協議之上的“無連接”協議,每次連接只處理一個請求。在BOA服務器上,運行產著一個守護進程對端口進行監聽,等待來自客戶的請求。當一個請求到來時,將創建一個子進程為用戶的連接服務。根據請求的不同,服務器返回HTML文件或者通過CGI調用外部應用程序,返回處理結果。服務器通過CGI與外部程序和腳本之間進行交互,根據客戶端在進行請求時所采取的方法,服務器會收集客戶所提供的信息,並將該部分信息發送給指定的CGI擴展程序。CGI擴展程序進行信息處理並將結果返回服務器,然後服務器 對信息進行分析,並將結果發送回客戶端。
外部CGI程序與BOA服務器進行通信、傳遞有關參數和處理結果是通過環境變量、命令行參數和標準輸入來進行的。服務器提供了客戶端(瀏覽器)與CGI擴展程序之間的信息交換的通道。CGI的標準輸入是服務器的標準輸出,而CGI的標準輸出是服務器的標準輸入。客戶的請求通過服務器的標準輸出傳送給CGI的標準輸入,CGI對信息進行處理後,將結果發送到它的標準輸入,然後由服務器將處理結果發送給客戶端。
CGIC是一個功能比較強大的支持CGI開發的標準C庫,並支持Linux,Unix 和Windows等多操作系統。
CGIC的主站點http://www.boutell.com/cgic/
3、URL簡介
客戶端瀏覽器向服務器發送數據采用編碼的形式進行,編碼就是URL編碼。編碼的主要工作是表單域的名字和值的轉義,具體的做法為:每一對域和值裏的空格都會被替換為一個加號(+)字符,不是字母或數字的字符將被替換為它們的十六進制數字形式,格式為%HH。HH是字符的ASCII十六進制值。
標簽將被替換為“%0D%0A”。
信息是按它們在表單裏出現的順序排列的。數據域的名字和數據域的值通過等號(=)字符連在一起。各對名/值再通過“&”字符連接在一起。經過這些編碼處理之後,表單信號就整個成為一個連續的字符流,裏面包含著將被送往服務器的全部信息。
因為表單輸入信息都是經過編碼後傳遞給腳本程序的,所以CGI擴展程序在使用這些參數之前必須對它們進行解碼。
二、CGIC編譯配置
1、下載CGIC源碼
tar -zxvf cgic206.tar.gz
2、修改Makefile文件
CC=arm-none-linux-gnueabi--gcc
AR=arm-none-linux-gnueabi--ar
RANLIB=arm-none-linux-gnueabi-ranlib
CFLAGS=-g -Wall -static
cgictest.cgi: cgictest.o libcgic.a
$(CC) $(CFLAGS) cgictest.o -o cgictest.cgi ${LIBS}
capture: capture.o libcgic.a
$(CC) $(CFLAGS) capture.o -o capture ${LIBS}
3、編譯
make
編譯得到的文件
libcgic.a:CGIC庫
capture:調試輔助程序
cgictest.cgi:測試程序
4、安裝CGIC
make install
CGIC安裝路徑為
libcgic.a 安裝在/usr/local/lib
cgic.h 安裝在/usr/local/include
CGIC庫安裝後就可以使用CGIC編程了
5、CGIC文件的移植
將capture和cgictest.cgi拷貝到開發板的/var/www/cgi-bin目錄
6、運行cgi程序
在客戶端瀏覽器運行http://192.168.6.210/cgi-bin/cgictest.cgi
如果正常顯示網頁內容,則BOA與CGIC可以正常工作
三、CGIC移植過程中錯誤的解決
1、html網頁可以運行,CGI程序運行報錯
Boa服務器報錯:cgi_header: unable to find LFLF
客戶端瀏覽器報錯:502 Bad Gateway
The CGI was not CGI/1.1 compliant.
解決方法:靜態編譯cgi程序
arm-linux-gcc -o hello.cgi hello.c -static
四、CGIC編程
1、CGI通信方式
當有數據從客戶端瀏覽器傳到Web服務器後,web服務器會根據傳送的類型(基本有二類:GET/POST),將接收到的數據傳入 QUERY_STRING或變量中, CGI程序可以通過標準輸入,在程序中接收web服務器接收的數據。當要向瀏覽器發送信息時,只要向Web服務器發送特定的文件頭信息,即可通過標準輸出將信息發往Web服務器, Web服務器處理完由CGI程序發來的信息後就會將信息發送給瀏覽器。
2、接收數據
用GET方式接收到的數據保存在Web服務器的QUERY_STRING 變量裏,而通過POST方式接收到的數據是保存在Web服務器變量裏。兩種數據接收方式的區別是:以GET方式接收的數據是有長度限制,而用POST方式接收的數據是沒有長度限制的;以GET方式發送數據,可以通過URL的形式來發送,但POST方式發送的數據必須要通過Form才到發送。
3、CGI變量
char *cgiServerSoftware
服務器軟件名稱,或者一個空的字符串
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請求或者
char cgiRemoteHost
從瀏覽器返回客戶主機的名字
char cgiRemoteAddr
從瀏覽器返回客戶的IP地址
char cgiAuthType
返回用戶授權信息
char cgiRemoteUser
鑒別用戶cgiAuthType.
char cgiRemoteIdent
返回用戶的名字(用戶通過用戶堅定協議)
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輸入
4、CIGC庫主要函數
用一般 ANSI C或C++編譯器就可以編譯CGIC程序 , 與C程序不同的是,用CGIC寫的源碼其主函數是cgiMain(), 而不是通常的main。 CGIC的函數庫會自動把cgiMain連接到相應的main上。
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環境以便用來調試。
5、CGI結果編碼
CGIC結果編碼參考:
cgiFormSuccess
提交信息成功
cgiFormTruncated
刪除部分字節.
cgiFormBadType
錯誤的輸入信息(沒有按要求)
cgiFormEmpty
提交信息為空.
cgiFormNotFound
提交信息沒有找到.
cgiFormConstrained
數字屬於某個特定的範圍,被迫低於或高於適當範圍。
cgiFormNoSuchChoice
單一選擇提交的值是不被接受。通常說明表但和程序之間存在矛盾。
cgiEnvironmentIO
從CGI環境或獲取的文件讀或寫的企圖失敗,報出I/O的錯誤。
cgiEnvironmentMemory
從CGI環境或獲取的文件讀或寫的企圖失敗,報出out-of-memory的錯誤。
cgiEnvironmentSuccess
從CGI環境或獲取的文件讀或寫的企圖成功。
6、CGI環境變量
REQUEST_METHOD 請求類型,如“GET”或“POST”
CONTENT_TYPE 被發送數據的類型
CONTENT_LENGTH 客戶端向標準輸入設備發送的數據長度,單位為字節
QUERY_STRING 查詢參數,如“id=10010&sn=liigo”
SCRIPT_NAMECGI 腳本程序名稱
PATH_INFOCGI 腳本程序附加路徑
PATH_TRANSLATEDPATH_INFO 對應的絕對路徑
REMOTE_ADDR 發送此次請求的主機IP
REMOTE_HOST 發送此次請求的主機名
REMOTE_USER 已被驗證合法的用戶名
REMOTE_IDENTWEB 服務器的登錄用戶名
AUTH_TYPE 驗證類型
GATEWAY_INTERFACE 服務器遵守的CGI版本,如:CGI/1.1
SERVER_NAME 服務器主機名、域名或IP
SERVER_PORT 服務器端口號
SERVER_PROTOCOL 服務器協議,如:HTTP/1.1
DOCUMENT_ROOT 文檔根目錄
SERVER_SOFTWARE 服務器軟件的描述文本
HTTP_ACCEPT 客戶端可以接收的MIME類型,以逗號分隔
HTTP_USER_AGENT 發送此次請求的web瀏覽器
HTTP_REFERER 調用此腳本程序的文檔
HTTP_COOKIE 獲取COOKIE鍵值對,多項之間以分號分隔,如:key1=value1;key2=value2
芯靈思Sinlinx A33開發板boa與CGI移植