1. 程式人生 > >實現類似路由器那樣通過WEB頁面配置裝置

實現類似路由器那樣通過WEB頁面配置裝置

實現通過web來與其他程序互動

        要像路由器那樣,通過訪問一個網頁就能配置裝置,實質上是web伺服器與其他程序的通訊問題。關鍵在於,我們的web伺服器收到瀏覽器的請求時能夠通知另外一個程序來處理。通常配置一個裝置的程式可以用高階或者低階語言來實現,但是苦於不知道我們何時配置或者配置什麼,而只有與使用者互動的web頁面知道。只要我們的web頁面能夠告訴我們的配置程序要配置的引數,那麼問題就迎刃而解。

        最簡單的就是通過資料庫來實現,因為web程式都支援資料庫,當收到使用者請求時,把要配置的引數等都寫入資料庫,並寫入一個標誌,而我們的配置程序由定時器觸發不斷地讀取資料庫,當發現標誌被改了,再讀取配置引數進行相應的配置。這是最簡單的一種結構。

         另一種就是web程式通過socket套接字與其他程序互動。socket套接字的應用廣泛而且跨平臺得很,只要你的網頁的伺服器程式碼能支援socket基本上就完事了,像PHP、C#等都比較好做。偏偏我只會一點c和c++....儘量找能支援c或c++的做法。果然讓我找到fastCGI。官網是www.fastcgi.com。裡面都有詳細的介紹等。fastCGI說白了其實是實現一個main函式。在這個main函式裡,沒有請求的時候就阻塞,有請求的時候,函式就接著執行,通常是一個迴圈不斷等待來自瀏覽器的請求。而對於每個請求,伺服器以及客戶端的很多資訊都可以通過request的環境變數獲得,例如該請求是GET還是POST等。而客戶發過來的資料(例如POST)就通過標準stdin例如getchar()等得到。至於對於請求怎麼響應你完全可以用熟悉的c來做。最後要返回給使用者的html程式碼通過stdout來輸出給使用者。例如printf()、fprintf()等。

        很多網路伺服器都支援fastCGI,我使用的是在linux平臺的apache伺服器,一切用到的都支援跨平臺。當然apache伺服器太強大了,如果像路由器這樣的嵌入式裝置還是採取輕量級的伺服器好些。apache伺服器接收來自客戶瀏覽器browser的請求,再把請求轉由fastCGI程序來處理。fastCGI程式支援多種語言如perl,php,java,c,c++等,既然可以用C語言來實現,那就是說可以在fastCGI函式裡面使用 socket等等等等為所欲為,通過socket,fastCGI就能於其他程序或者其他機器上的程序通訊。

inux下配置apache伺服器、fastCGI模組以及 fastCGI development kit

         配置apache伺服器的fastCGI模組花了我好幾天的時間,一直在嘗試把fast-dev-kit裡面的例子跑起來,主要是配置問題。解壓httpd-2.2.19.tar.gz(apache_http伺服器)、mod_fcgid- 2.3.6(apache_http伺服器的fastcgi模組)、fcgi-2.4.0(fastCGI c或c++的開發包),裡面有readMe檔案也有介紹。先在httpd-2.2.19下./configure預設配置.再make,make install安裝好apache伺服器。安裝完後會在/usr/local/下出現apache2目錄,在usr/local/apache/bin下啟動apache伺服器,./apachectl -k start。在瀏覽器輸入localhost看apache伺服器是否成功啟動。接著安裝mod_fcgid-2.3.6,在其目錄下有個 configure.apxs檔案,需要通過apache2/bin/下的apxs程式來配置,執行APX=/usr/local/apache2 /bin/apxs ./configure.apxs來配置fastCGI模組,接著make,makeinstall安裝。安裝完後會在apache/modules/下多了mod_fcgid.so檔案,說明fastCGI模組已安裝成功。fastCGI開發包同樣也是./configure、make、make install安裝。

用C語言實現一個fastCGI程式:

程式如下:
#include "fcgi_stdio.h"
#include <stdlib.h>

   int main()
   {
       int count = 0;
       while(FCGI_Accept() >= 0)
           printf("Content-type: text/html\r\n"
                  "\r\n"
                  "<title>FastCGI Hello!</title>"
                  "<h1>FastCGI Hello!</h1>"
                  "Request number %d running on host <i>%s</i>\n",
                   ++count, getenv("SERVER_NAME"));

    return 0;
   }

        當用戶有請求時,FCGI_Accept()就會返回。這裡的printf是fcgi_stdio.h裡的printf,printf的內容會返回給使用者。程式的編譯需要開發包下的/libfcgi/下的fcgi_stdio.c、 fcaiapp.c、和os_unix.c。用g++來編譯。還要把開發包下的include檔案加入路徑。編譯可能會報錯因為socklen_t的問題。看了os_unix.c的原始碼之後你就明白了,我為求方便加了個巨集定義就編譯通過了。生成程式tiny.fcg。
        接下來得配置apache伺服器,使它可以執行我們的tiny.fcg程式。apache的伺服器的配置都在/conf/下的httpd.conf檔案裡。
在裡面新增如下資訊:

LoadModule fcgid_module modules/mod_fcgid.so

<Directory "/usr/local/apache2/fcgi-bin">
    SetHandler fcgid-script
    AllowOverride None
    Options None
    Order allow,deny
    Allow from all
</Directory>

          大家注意了,我之所以花了幾天時間fastCGI程式都跑不起來就是因為SetHandler項,我一直寫了fcgi-script!!!少了一個d。我還重灌了兩遍apache..最後終於讓我發現了。一改為fcgid-script。。終於可以了!當訪問出錯的時候,apache會把錯誤記錄在apache/logs/下的error_log和access_log,當你除錯fastCGI程式的時候,這兩個檔案給你很大幫助。

          apache的配置檔案裡的語法官網都有介紹,要是你配置了還跑不起來,你就得像我那樣去啃官網的英文文件了看天意了。。。。
          配置好後fcgi-bin資料夾裡面的程式收到請求時都自動呼叫fastCGI來跑。假設我們裡面有tiny.fcg檔案。那麼訪問localhost/fcgi-bin/tiny.fcg時,請求就交由tiny.fcg處理了。正如我們c語言實現那樣網頁返回以下內容:
FastCGI Hello!
Request number 1 running on host localhost
重新整理幾次之後,number會變化。

        整個過程,我們充當一個web伺服器的角色,對於使用者的請求,由我們來生成html頁面,然後把html原始碼通過printf或其他stdout返回給使用者。還得熟悉下html以及javascript指令碼語言這程式才好寫啊。

fastCGI如何獲得使用者GET或者POST過來的資料

有程式碼有真相:先貼再解釋


#include "fcgi_config.h"
#include "fcgiapp.h"
#include <sys/types.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif


int main(void)
{

    int rc;

    FCGX_Init();
    pid_t pid = getpid();
    FCGX_Request request;
    char *server_name;

    FCGX_InitRequest(&request, 0, 0);

    for (;;)
    {
        rc = FCGX_Accept_r(&request);

        if (rc < 0)
            continue;

        char buffer[1024*10];
        buffer[0] = '\0';
        char** p = request.envp;
        while(*p)
        {
            strcat(buffer,*p);
            strcat(buffer,"\r\n");
            p++;
        }

        server_name = FCGX_GetParam("SERVER_NAME", request.envp);

        FCGX_FPrintF(request.out,
            "Content-type: text/html\r\n"
            "\r\n"
            "<title>FastCGI Hello!</title>"
            "<h1>FastCGI Hello! </h1>"
             "<p>Process %ld</p>"
            "<p> host<i>%s</i></p>"
            "the environments we get is: <p>"
            "<pre>%s</pre>",
            pid, server_name ? server_name : "?",buffer);


        //
        char* method = FCGX_GetParam("REQUEST_METHOD",request.envp);
        if(strcmp(method,"POST") == 0)
        {
            char postedData[1024*10];
            int dataNum = 0;
            int ch;
            while((ch = FCGX_GetChar(request.in)) != -1)
            {
                postedData[dataNum++] = (char)ch;
            }

            postedData[dataNum] = '\0';

            FCGX_FPrintF(request.out,
                         "<p>the data posted is:</p>"
                         "<pre>%s</pre>",postedData);
        }

        FCGX_Finish_r(&request);

    }
        return 0;
}

      基本上你看到函式名字大概就能猜到了,並且可以看原始碼的。首先看以下 FCGX_Request的結構。

typedef struct FCGX_Request {
    int requestId;            /* valid if isBeginProcessed */
    int role;
    FCGX_Stream *in;
    FCGX_Stream *out;
    FCGX_Stream *err;
    char **envp;

    /* Don't use anything below here */

    struct Params *paramsPtr;
    int ipcFd;               /* < 0 means no connection */
    int isBeginProcessed;     /* FCGI_BEGIN_REQUEST seen */
    int keepConnection;       /* don't close ipcFd at end of request */
    int appStatus;
    int nWriters;             /* number of open writers (0..2) */
    int flags;
    int listen_sock;
} FCGX_Request;

       裡面的in就是stdin,可以讀取由瀏覽器POST過來的資料。out就是輸出,往out printf的內容將返回給瀏覽器使用者。envp就是字串陣列的指標,裡面儲存的都是環境變數,原始碼就把這些環境變數都打印出來了。把fastCGI程式(我起名為learnFCGI)放入apache伺服器的fcgi-bin資料夾(已配置好)。我在瀏覽器訪問localhost/fcgi-bin/learnCGI得到以下內容

FastCGI Hello!

Process 12660

hostlocalhost

the environments we get is:FCGI_ROLE=RESPONDER

HTTP_HOST=localhost

HTTP_USER_AGENT=Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100626 SUSE/3.6.6-1.2 Firefox/3.6.6

HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

HTTP_ACCEPT_LANGUAGE=en-us,en;q=0.5

HTTP_ACCEPT_ENCODING=gzip,deflate

HTTP_ACCEPT_CHARSET=ISO-8859-1,utf-8;q=0.7,*;q=0.7

HTTP_CONNECTION=close

PATH=/usr/lib64/mpi/gcc/openmpi/bin:/sbin:/usr/sbin:/usr/local/sbin:/root/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/X11R6/bin:/usr/games:/usr/lib/mit/bin:/usr/lib/mit/sbin

SERVER_SIGNATURE=

SERVER_SOFTWARE=Apache/2.2.19 (Unix) mod_fcgid/2.3.6

SERVER_NAME=localhost

SERVER_ADDR=::1

SERVER_PORT=80

REMOTE_ADDR=::1

DOCUMENT_ROOT=/usr/local/apache2/htdocs

[email protected]

SCRIPT_FILENAME=/usr/local/apache2/fcgi-bin/learnFCGI

REMOTE_PORT=45762

GATEWAY_INTERFACE=CGI/1.1

SERVER_PROTOCOL=HTTP/1.1

REQUEST_METHOD=GET

QUERY_STRING=

REQUEST_URI=/fcgi-bin/learnFCGI

SCRIPT_NAME=/fcgi-bin/learnFCGI

       由於我是直接訪問的,所以即使REQUEST_METHOD為GET,QUERY_STRING也為空,另外也沒有POST的資料,不妨寫個簡單的HTML頁面測試下。

程式碼如下:

<html>
<body>
<form action="/fcgi-bin/learnFCGI" method="get">
    name:<input type="text" name="myName" ></br>
    passwrod:<input type="text" name="myPassword"></br>
    <input type="submit" value="提交">
</form>
</body>
</html>

         把html儲存為testpage.html放在apache的usr/local/apache2/htdocs目錄下。就能訪問了,我這裡通過localhost/testpage.html得到

         往文字框裡輸入testname,testpassword.按提交則會轉到learnFCGI程式。我們會發現,這次列印的環境變數的QUERY_STRING發生了變化:

QUERY_STRING=myName=testname&myPassword=testpassword

我們的內容提交到fastCGI程式了。

把html程式碼裡<form> 中的method屬性改為 method="post",再次執行就能看到POST資料了


REQUEST_METHOD=POST

QUERY_STRING=

REQUEST_URI=/fcgi-bin/learnFCGI

SCRIPT_NAME=/fcgi-bin/learnFCGI


the data posted is:

myName=testname&myPassword=testpassword

        在使用fastCGI的過程中,網上的資料不是很多。。基本上我就去官網啃英文,啃了好幾遍還的不出個怎麼寫法。只好去試。。最重要的是多寫小程式來測試。。好像FCGX_Request裡面的環境變數有什麼內容本來我也不知道,那就寫個程式把裡面的都打印出來。。發現原來裡面有個REQUEST_METHOD和QUERY_STRING選項,我就知道可以往fastCGI提交資料。並嘗試把getchar()驗證是否能獲得POST的資料。

      這樣,通過fastCGI,web程式就能把資料提交給另外一個程序處理了。