引言
控制網(wǎng)絡一般指以控制“事物對象”為特征的計算機網(wǎng)絡系統(tǒng),簡稱Infranet(infrastructure network),,它處在企業(yè)網(wǎng)(Intranet)的底層,,構成了整個企業(yè)網(wǎng)的基礎。近些年來,,在控制網(wǎng)絡中采用了現(xiàn)場總線(Field Bus)和工業(yè)以太網(wǎng)(Ethernet)兩種技術,。出現(xiàn)這種情況主要有兩個原因:第一,目前存在多種現(xiàn)場總線標準,,不同的標準采用完全不同的通信協(xié)議,,也就是說現(xiàn)場總線的開放性不夠;第二,,以太網(wǎng)雖然能夠解決開放性的問題,,并具備成本低廉、技術成熟等優(yōu)點,,但由于以太網(wǎng)最初是被設計用于以資源共享為目的的計算機局域網(wǎng),,因此在實時性和可靠性上暫時還不能完全滿足工業(yè)控制的要求。事實上,,目前一個較大規(guī)模的控制網(wǎng)絡往往綜合采用了這兩種技術,。在現(xiàn)場層,由于強調實時性,、可靠性和安全性,,常采用現(xiàn)場總線技術;在監(jiān)控管理層,,考慮到采用不同標準的控制網(wǎng)段之間的集成以及與高層企業(yè)信息網(wǎng)絡的集成,,一般采用以太網(wǎng)技術。現(xiàn)場層的現(xiàn)場總線控制系統(tǒng)FCS(Field Control System)或其它設備級輕質網(wǎng)絡通過網(wǎng)關或嵌入式HTTP服務器與高層以太網(wǎng)相連,。這樣,,不僅采用不同協(xié)議的控制網(wǎng)段能夠實現(xiàn)互聯(lián),而且各個控制網(wǎng)段能方便地與高層企業(yè)信息網(wǎng)互通,從而最終實現(xiàn)企業(yè)網(wǎng)的管控一體化和對現(xiàn)場設備的Internet遠程監(jiān)控,。通過以上分析可以看出,,控制網(wǎng)絡中的網(wǎng)關或嵌入式HTTP服務器起著連接現(xiàn)場層和監(jiān)迭管理層的作用,因此它是整個控制網(wǎng)絡的關鍵設備,。
網(wǎng)關或嵌入式HTTP服務器與傳統(tǒng)的嵌入式系統(tǒng)相比,,有一些不同的特性。傳統(tǒng)的嵌入式系統(tǒng)是面向應用,、有一些不同的特性,。傳統(tǒng)的嵌入式系統(tǒng)是面向應用、面向產品的,,強調成本和高效設計系統(tǒng),,因此本質上不具備通用性和可移植性。網(wǎng)關或嵌入式HTTP服務器由于處在現(xiàn)場層和監(jiān)控管理層的中間,,因此它與具體應用和產品是一種弱耦合的關系,。同時,技術發(fā)展的趨勢是:硬件成本越來越低,,功能越來越強,,越來越多的芯片和板卡具備“平臺”的特點,適用于多種應用場合,。嵌入式實時操作系統(tǒng)(Embedded Real Time Operationg System)的發(fā)展更是為嵌入式軟件提供了一個通用的軟件平臺,。綜上所述,在網(wǎng)關或嵌入式HTTP服務器設計中,,考慮通過選用適合的硬件和嵌入式實時操作系統(tǒng),,使整個系統(tǒng)具備相當?shù)耐ㄓ眯院涂梢浦残浴τ谶B接不同的設備級輕質網(wǎng)絡或不同的應用,,只需要通過更換硬件模塊和對代碼作最小的修改即可實現(xiàn),。
1 基于Linux的嵌入式HTTP服務器的結構
為了實現(xiàn)設計目標,嵌入式HTTP服務器一般應采用功能較強的能用PC,、工業(yè)PC,、或高檔MPU作為硬件平臺,嵌入式實時操作系統(tǒng)作為軟件平臺進行平發(fā),。硬件平臺應具備以太網(wǎng)口和一個或多個通信模塊,,比如RS232、RS485,、CAN通信卡等。嵌入式實時操作系統(tǒng)實現(xiàn)了TCP/IP等網(wǎng)絡協(xié)議,,并提供實時任務,、進程管理、內存管理、文件系統(tǒng),、API等功能,。
Linux操作系統(tǒng)是一種多進程,多用戶的通用操作,。由于它具備免費,、源碼公開、內核可裁減,、支持多線程,、網(wǎng)絡功能強大、設計精巧,、性能穩(wěn)定的特點,,因此近年它也被廣泛用到嵌入式系統(tǒng)的設計中。一個應用于嵌入式系統(tǒng)的Linux經(jīng)過裁減和重新編譯后只包括進程管理,、內存管理,、文件系統(tǒng)、若干個驅動程序和實用的函數(shù)等,。
下面以本人參與的轉子秤控制系統(tǒng)為例來說明一個基于Linux的嵌入式HTTP服務器的結構,。轉子秤是水泥工業(yè)中的關鍵計量喂料設備,一條大型的生產線需要許多臺轉子秤,,對轉子秤的控制涉及到重量,、轉速、溫度,、一氧化碳含量等若干個參量,。由于現(xiàn)場環(huán)境的高噪聲、高粉塵,、高電磁干擾,,無法在現(xiàn)場配備鍵盤、顯示器,、觸摸屏等人機交互設備,,無法在現(xiàn)場實現(xiàn)對設備的監(jiān)控和維護。同時,,一條生產線有多臺轉子秤,,為每臺轉子秤配備人機交互設備也是不經(jīng)濟的。為此,,考慮為整個系統(tǒng)設計一個嵌入式HTTP服務器,,各轉子秤控制器與嵌入式HTTP服務器用CAN總線相連。通過嵌入式HTTP服務器實現(xiàn)對整個系統(tǒng)的在線監(jiān)控和遠程監(jiān)控,。在嵌入式HTTP服務器的設計中,,選用研祥公司PC104總線的486X嵌入式CPU卡作為硬件平臺,該板卡是具有128MB的在板ROM、CF卡接口和以太網(wǎng)接口等,。選擇該板卡的原因是PC104總線的功能擴展模塊非常豐富,,通過選擇不同的模塊很容易就支持多種總線。軟件平臺方面,,選用Linux2.0內核并對它作適當裁減,。整個嵌入式HTTP服務器的結構簡圖如圖1所示。
2 基于Linux的嵌入式HTTP服務器的設計
工控領域的嵌入式HTTP服務器應該具備如下基本功能,。
①實時數(shù)據(jù)發(fā)布,。實時數(shù)據(jù)主要包括系統(tǒng)運行過程中設備的各種狀態(tài)信息。嵌入式HTTP服務器將實時數(shù)據(jù)以網(wǎng)頁形式發(fā)布到Internet上,,且動態(tài)實時刷新,。客戶可以通過瀏覽器訪問這些實時信息,。
②參數(shù)設置,。參數(shù)包括運行參數(shù)和設備狀態(tài)參數(shù),如各種初始值,、常數(shù)等,。嵌入式HTTP服務器接收到客戶提交的參數(shù)設備請求后,執(zhí)行參數(shù)寫入操作,。
③遠程實時控制,。遠程實時控制允許遠程用戶在線地控制系統(tǒng)中的相應執(zhí)行機構,比如電機,、電磁閥等,。嵌入式HTTP服務器接收到遠方客戶提交的控制操作請求后,將下發(fā)控制命令驅動監(jiān)控系統(tǒng)中相應的執(zhí)行機構,。
④訪問級別設置和權限認證,。只有權限不低于要求訪問級別的客戶,經(jīng)嵌入式HTTP服務器認證后,,方可進行其權限范圍內的監(jiān)控操作,。
3 主要實現(xiàn)技術
3.1 超文本傳輸協(xié)議
HTTP協(xié)議是一個面向事務、無狀態(tài)的應用層協(xié)議,。在傳輸層,,HTTP協(xié)議使用請求(request)/響應(response)模型。一次簡單的HTTP事務包括以下過程,。首先,,客戶(瀏覽器)發(fā)起和建立一條到服務器的TCP連接。然后,,客戶發(fā)送一個HTTP請求到服務器,,請求包含方法,、URI,、協(xié)議版本和一個類MIME報文,。服務器解析HTTP請求后,給出相應的HTTP響應,,響應包括協(xié)議版本,、狀態(tài)碼、解釋狀態(tài)碼的簡短短語和一個類MIME報文,。最后,,釋放TCP連接。Linux操作系統(tǒng)為用戶提供了稱為BSD Socket的網(wǎng)絡編程接口,。利用其中的TCP套接口函數(shù),,可以非常方便地實現(xiàn)HTTP協(xié)議。
HTTP1.0為每一次HTTP請求/響應建立一條新的TCP連接,,由于建立一條TCP連接要經(jīng)歷3次握手,,因此效率不高。HTTP1.1提出了可持續(xù)性連接的概念,。HTTP1.1只建立一次TCP連接,,而重復地使用它傳送一條素的請求/響應消息,減少了額外開銷,。在嵌入式HTTP服務器中,,一般使用HTTP1.1協(xié)議。HTTP1.1協(xié)議的細節(jié)請參考RFC2616,。
3.2 通用網(wǎng)關接口CGI
參數(shù)設置和遠程控制功能都是通過CGI(通用網(wǎng)關接口)程序和表單實現(xiàn)的,。CGI使用HTML表單向Web服務器發(fā)送信息?;菊Z法如下:
<FORM METHOD=get/post ACTION=URL></FORM>
其中,,METHOD屬性指定將數(shù)據(jù)傳送到Web服務器的方法。輸入方法有兩種:GET和POST,。ACTION屬性定義要對表單數(shù)據(jù)進行處理的CGI腳本的URL,。
CGI的工作流程是首先由瀏覽器將用戶輸入的數(shù)據(jù)傳遞給Web服務器,Web服務器根據(jù)接收到的數(shù)據(jù)設置環(huán)境變量并啟動CGI腳本,,CGI腳本從環(huán)境變量中讀取所需要的數(shù)據(jù)并進行相應處理,,最后使用STDOUT輸出HTML形式的結果文件,經(jīng)Web服務器送回瀏覽器,,最終顯示給用戶,。傳統(tǒng)的CGI程序與服務器代碼分開,是一個符號CGI標準的可執(zhí)行文件,,并儲存在CF卡等存儲設備上,,一般用腳本語言編寫,。考慮到嵌入式HTTP服務器要求速度快,,功能和代碼都盡可能精簡的特點,,可以把原先由可執(zhí)行文件完成的功能用C函數(shù)實現(xiàn),放在服務器代碼內部,,并直接從HTTP請求報文接收數(shù)據(jù),。與傳統(tǒng)CGI程序相比,這種方法具備如下特點:
*不需要標準輸入,,CGI函數(shù)可以直接獲取到瀏覽器送來的信息,;
*不需要標準輸出,CGI函數(shù)可以直接將數(shù)據(jù)送回給瀏覽器,;
*不需要環(huán)境變量,,CGI和Web服務器在同一程序中實現(xiàn),不需要環(huán)境變量來交換信息,。
3.3 自定義標記
要在網(wǎng)頁中顯示工控系統(tǒng)中大量的實時數(shù)據(jù),,常規(guī)方法是將HTML代碼直接集成到程序代碼中,或者反之將C程序代碼集成到HTML標記語言中,。這兩種方法均要求開發(fā)人員對HTML標記語言的語法細節(jié)非常熟悉,。網(wǎng)頁或程序結構的單方面調整都將導致整個系統(tǒng)全盤修改,系統(tǒng)不具備靈活性與可擴展性,。HTML的精髓在于該語言的“標記”性,,各種不同標記的具體含義是由服務器和瀏覽器進行解析。因此,,當現(xiàn)有標記不能滿足新的應用需求時,,可以自行定義新的標記,只需服務器將自定義標記解析為標準標記,,然后傳送給瀏覽器即可,。在本項目中,主要的實時數(shù)據(jù)轉速,、重量,、一氧化碳含量等狀態(tài)信息,可以定義相應的標記,。服務器中解析相應標記的函數(shù)同樣用C語言來實現(xiàn),。運行時,當客戶端發(fā)出查看某實時網(wǎng)頁的請求后,,嵌入式HTTP服務器將相應的網(wǎng)頁文件從電子盤加載到內存進行逐項解析,。當辨識出自定義標記后,就調用相應的函數(shù),。該函數(shù)返回該標記對應的當前值,,并置換HTML文件流中的自定義標記,。最后,嵌入式HTTP服務器將解析結果發(fā)送給客戶端,。實時網(wǎng)頁的設計與相應的HTTP服務器處理程序得以分離,,處于一種弱耦合關聯(lián)狀態(tài)。這樣,,網(wǎng)頁界面的調整不會影響HTTP服務器的程序設計,,HTTP服務器程序的修改也與網(wǎng)頁界面設計無關,,整個嵌入式HTTP服務器具備靈活性和可擴展性,。
3.4 多線程
最初的進程定義包含程序、資源及其執(zhí)行三部分,,其中程序通常指代碼,,資源通常包括 內存資源、I/O資源,、信號處理等,,而程序的執(zhí)行指執(zhí)行上下文,這一部分后來發(fā)展為線程,。在線程的概念出現(xiàn)以前,,為了減小進程切換的開銷,操作系統(tǒng)設計者逐漸修改正進程的概念,,允許將進程所占有的資源從其主體剝離出來,,允許某些進程共離享一部分資源,例如文件,、信號,、數(shù)據(jù)內存、甚至代碼,,這就是輕質進程的概念,。Linux內核的2.0.x版本就已經(jīng)實現(xiàn)了輕質進程。應用程序可以通過一個統(tǒng)一的clone()系統(tǒng)調用接口,,用不同的參數(shù)指定創(chuàng)建輕質進程還是普通進程,。在內核中,clone()調用經(jīng)過參數(shù)傳遞和解釋后會調用do_fork(),,這個核內函數(shù)同時也是fork,、vfork()系統(tǒng)調用的最終實現(xiàn)。在do_fork()中,,不同的flone_flags將導致不同的行為,。在LinuxThreads中,使用(CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND)參數(shù)調用clone()創(chuàng)建“線程”,,表示共享內存,、共享文件系統(tǒng),、共享文件描述符表,以及共享信號處理方式,。Linux操作系統(tǒng)下,,已經(jīng)實現(xiàn)基于輕質進程的符號POSIX1003.C標準的線程庫LinuxThreads。
在傳統(tǒng)的Unix服務器程序設計中,,為了使服務器具備并發(fā)處理連接的能力,,通常采用父進程處理連接,并調用fork()創(chuàng)建子進程來處理用戶請求的方法,。這種方法的缺點是進程創(chuàng)建慢,,耗費資源,,進程切換開銷大,,進程之間通信比較困難等,,不適用對資源,、速度有要求的嵌入式系統(tǒng)。因此,,在嵌入式HTTP服務器的開發(fā)中使用線程的方法。利用LinuxThreads提供的pthread_create()等函數(shù)派生出線程,,也即輕質進程來處理多個HTTP請求。
4 工作流程和代碼設計
4.1工作流程
嵌入式HTTP服務器程序開始運行時,,主進程首先創(chuàng)建一個接口,,并和主機地址綁定到一起,隨后置為被動監(jiān)聽狀態(tài),,等待客戶端連接請求的到來。分別用函數(shù)socket()創(chuàng)建一個接口,,bind()綁定地址,listen()監(jiān)聽,,accept()接收來完成,。當建立一個TCP連接后,,函數(shù)accept()返回一個新的套接口描述符,主進程就創(chuàng)建一個新的子線程(輕質進程)處理這個新的連接,。
子線程用于處理每具體的HTTP請求,。子線程首先解析用戶的HTTP請求。當用戶請求一個網(wǎng)頁時,,子線程查找文件系統(tǒng),。如果該網(wǎng)頁文件存在,且通過權限認證,,就把它從CF卡讀入內存并掃描,發(fā)現(xiàn)有自定義標記則調用相應函數(shù)進行處理,,最后把結果返回給瀏覽器,;否則給一個簡單的出錯消息。當用戶是上傳數(shù)據(jù)時,,子線程調用相應函數(shù)讀取數(shù)據(jù)進行處理,并返回處理結果給瀏覽器,。
4.2 代碼設計
在嵌入式HTTP服務器的代碼設計中,考慮到代碼的移植性和擴展性,,利用C語言實現(xiàn)了面向對象風格的代碼結構。代碼主要由兩上數(shù)據(jù)結構request_inf和response_inf以及其上一組操作函數(shù)組成,。
結構request_inf和response_inf分別用來保存HTTP請求報文和響應報文的所有信息。在結構定義時,,應根據(jù)具體應用特點設計結構包含的成分。
嵌入式HTTP服務器的函數(shù)包括通用函數(shù),、CGI函數(shù)和自定義標記處理函數(shù)等,,其中通用函數(shù)是一些與HTTP1.1協(xié)議有關的函數(shù),。
(1)通用函數(shù)
*void prase_request_line(char *,struct *request_inf)
該函數(shù)用來解析HTTP請求報文的請求行(Request_Line),,并把相應信息存放在結構request_inf中,。其中,對請求行中URI部分的解析包括兩種情況,。如果用戶請求一個網(wǎng)頁,則獲取文件路徑,、文件類型;如果用戶要求上傳數(shù)據(jù),,則把數(shù)據(jù)放在一個字符數(shù)組中。然后將文件路徑和類型,,或者指向該數(shù)組的指針,、方法,、版本號信息都放入結構request_inf中,。
*void prease_general_header(char*,struct*request_inf)
該函數(shù)用來解析HTTP請求報文的調用首部(General_Header),。之所以把此函數(shù)與函數(shù)prase_request_line()分開,是考慮到程序的修植性和擴展性,。請求行和通用首部是請求報文中的不同部分,不不同的場合下,,要求解析的信息可能存在差導師。同時,,這樣也能使程序結構更清楚。比如,本項目要從通用首部解析字段Keep_Alive,。該字段指明一個最長的時間或最大請求數(shù)目,在此范圍內可以保持TCP連接不被釋放(即前文提到的HTTP1.1的持續(xù)連接特性,,persistent connection)。
*void prase_request_header(char*,struct*request_inf)
void prase_entity_header(char*,struct*request_inf)
HTTP請求報文的請求頭部用來說明瀏覽器的一些信息,,實體頭部則用來說明請求報文中可能存在的實體主體信息。本項目實際上并不需要使用這兩個函數(shù)來獲取相關信息,,但考慮到程序的擴展性和移植性,此處仍然把它列出來,,它們是兩個空函數(shù)。
*send_status_line(int fd,struct *response_inf)
此函數(shù)用來產生一個HTTP響應報文的狀態(tài)行(Status_line),。狀態(tài)行包括三部分內容,,即HTTP版本、狀態(tài)碼以及解釋狀態(tài)碼的簡單短語,。這些信息預先放在結構response_inf中,。
*send_general_header(int fd,struct*response_inf)
send_response_header(int fd,struct*response_inf)
send_entity_header(int fd,struct*response_inf)
這三個函數(shù)分別用來產生HTTP響應報文的通用首部,、響應首部(Response_header)和實體首部,。嵌入式HTTP服務器是一個瘦服務器,功能非常簡單,。因此HTTP響應報文的通用首部、響應首部和實體首部中的可選字段許多是不需要的,,還有許多是固定不變的,例如Last_modified和Content_type字段。Last_modified字段指出資源上次被修改的時間并由接收方解釋,。如果接收方已有此資源的拷貝,但此拷貝比Last-Modified域所指定的要舊,,那該拷貝就是過期的。由于網(wǎng)頁文件中含有自定義標記,,具有實時性,所以此字段根本沒有含有Content_type字段指出實體的媒體類型,,本項目中的嵌入式HTTP服務器被設計成只支持HTML類型,,因此該字段的內容總是Content_type=text/html。有關服務器和資源的所有標題域信息都被放入結構response_inf中,。
*send_white_line(int fd)
此函數(shù)用于實體首部和實體之間傳送一個空白行。
*void send_entity_body(int fd,char *buff_file)
此函數(shù)用來傳遞實體主體,,實體主體實際上是一個處理后的網(wǎng)頁文件,,它被放在指針buff_file指向的緩沖區(qū)內。
*void zero_request_inf(struct*request_inf)
void zero_response_inf(struct*response_inf)
這兩個函數(shù)用于結構request_inf和response_inf清零,。
*void get_file(struct*request_inf,struct * response_inf,char*buff_file,void*,void*)
該函數(shù)用來處理用戶HTTL請求,。首先,函數(shù)會檢查request_inf結構,,判斷用戶是請求一個網(wǎng)頁文件還是上傳數(shù)據(jù),。當用戶請求網(wǎng)頁文件時,函數(shù)將根據(jù)request_inf結構中的文件路徑信息,,在文件系統(tǒng)錄找此文件,。如果文件不存在或不具備權限,,則函數(shù)將狀態(tài)碼和解釋短語寫入結構response_inf,,然后直接返回,;否則讀取文件并調用自定義標記處理函數(shù),對標記進行處理,,處理過的網(wǎng)頁文件被放入buff_file指向的緩沖區(qū)內,并把狀態(tài)碼,、解釋短路和與實體有關的一些信息寫入結構response_inf。當用戶上傳數(shù)據(jù)時,,該函數(shù)調用CGI處理函數(shù)向CAN總線網(wǎng)絡發(fā)送幀,然后將狀態(tài)碼和解釋短路寫入結構response_inf,。利用狀態(tài)碼和解釋短語只能用“200,OK”或“500,Internal Server Error”等,,簡單反映執(zhí)行情況,。用戶要獲取詳細信息,可待一段合適的時間后請求網(wǎng)頁文件。函數(shù)中兩個void指針分別指向自定義標記處理函數(shù)和CGI處理函數(shù),,或者對應的函數(shù)指針數(shù)組,。
(2)自定義標記處理函數(shù)和CGI處理函數(shù)
自定義標記處理函數(shù)用于對自定義的處理,,每一類自定義標記對對應一種自定義標記處理函數(shù),同一類自定義標記的不同數(shù)據(jù)點利用參數(shù)來區(qū)分,,比如轉子秤1的重量標記可以用weight1來表示。所有的自定義標記處理函數(shù)被放在一起,,構成一個函數(shù)指針數(shù)組。自定義標記處理函數(shù)向CAN總線網(wǎng)絡發(fā)送遠程幀和接收數(shù)據(jù)幀,,獲取相應的狀態(tài)信息。CGI總線網(wǎng)絡發(fā)送遠程幀和接收數(shù)據(jù)幀,,獲取相應的狀態(tài)信息。CGI處理函數(shù)用變量名來區(qū)分,,同一類變量對應一種CGI處理函數(shù)。與自定義標記處理函數(shù)類似,,所有的CGI處理函數(shù)也被放在一起,,構成一個函數(shù)指針數(shù)組。由于自定義標記函數(shù)和CGI處理函數(shù)類型眾多,,這里就不列舉了。
結語
我們設計的嵌入式HTTP服務器具備良好的通用性和可移植性,。通過更換或增加PC104通信模塊,,該服務器能夠支持不同的現(xiàn)場總線,或同時連接幾種不同的設備級輕質網(wǎng)絡,。同時在服務器代碼設計中,用C語言實現(xiàn)了面向對象風格的代碼結構,。這樣,如果要求服務器端具備更多的特性,,只需要簡單修改結構request_inf,、response_inf,、操作函數(shù)和網(wǎng)頁文件即可達到目的,。這種設計思路不僅適用于嵌入式HTTP服務器,隨著硬件技術尤其是嵌入式操作系統(tǒng)技術的發(fā)展,,它同樣能夠應用到其它嵌入式產品的開發(fā)中,。