appweb的JavaScript调用C函数的实现过程(以设置eth0 IP地址为例)

 appweb的独到之处:EJS调用底层的C函数的实现代码:
appweb-src-2.4.1\doc\guide\appweb\users\ejs\extending.html
appweb-src-2.4.1-latest\appweb-src-2.4.1\doc\api\gen\appweb\overview.html

可以结合:   源码\samples\C++\simpleModule以及  源码\samples\C\simpleEsp目录里的 文件来学习。

C++ 代码(ethernetModule.cpp):  

#include        "ethernetModule.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <sys/ioctl.h>
#include <net/if.h>


static int set_ipaddr(const int sockfd, const char *ifname, char *ip);
static int set_netmask(const int sockfd, const char *ifname, char *netmask);

int mprEthernetModuleInit(void *handle)
{
        new EthernetModule(handle);
        return 0;
}

EthernetModule::EthernetModule(void *handle) : MaModule("ethernetModule", handle)
{
               //这里将我们的C函数setEthernet()与EJS(Embedded JavaScript)函数setEthernet()绑定,这样我们在ESP就可以调用底层的C函数了。
                espDefineStringCFunction(0, "setEthernet", setEthernet, 0);  
}

EthernetModule::~EthernetModule()
{
}

int EthernetModule::parseConfig(char *key, char *value, MaServer *server,
        MaHost *host, MaAuth *auth, MaDir* dir, MaLocation *location)
{
        if (mprStrCmpAnyCase(key, "ethernetDirective") == 0)
                return 1;
        return 0;
}

int EthernetModule::start()
{
        return 0;
}

void EthernetModule::stop()
{
}

/*
* Function                :        Set IP address, netmask for eth1
* Usage                :        setEthernet(ipaddr, netmask);
* Return value        :          MprVar status
*/

static int setEthernet(EspRequest *ep, int argc, char **argv)
{
        char                    *ifname="eth1";
        char                    *ip, *netmask;
               int                     sockfd, ret;
                struct                 in_addr        addr;       
                MprVar                 status;
                status = espCreateObjVar("setStatusObj", 0);

                ip = argv[0];
                netmask = argv[1];
               
                if( (ret=inet_pton(AF_INET, ip, &addr)<=0) || (ret=inet_pton(AF_INET, netmask, &addr)<=0) )
                {   
                        status = mprCreateIntegerVar(0);               
                        goto ret;
                }

                if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
                {   
                        status = mprCreateIntegerVar(0);               
                        goto ret;
                }

                if ((!set_ipaddr(sockfd, ifname, ip)) || (!set_netmask(sockfd, ifname, netmask)) )
                {   
                        status = mprCreateIntegerVar(0);               
                        goto ret;
                }
                 
                status = mprCreateIntegerVar(1);
               
                ret:
                espSetReturn(ep, status);
                espDestroyVar(&status);

                return 0;
}


static int set_ipaddr(const int sockfd, const char *ifname, char *ip)
{
        struct  sockaddr_in     addr;
        struct  ifreq           ifr;
                int                ret = 1;  //set status:   0->Failture 1->Success

        bzero(&addr, sizeof(addr));
        addr.sin_family = AF_INET;
                inet_aton(ip,&addr.sin_addr);

        strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);

        memcpy(&ifr.ifr_addr,(struct sockaddr *)&addr, sizeof(addr));

        if(ioctl(sockfd, SIOCSIFADDR, &ifr) == -1)
                                ret = 0;

        return ret;
}


static int set_netmask(const int sockfd, const char *ifname, char *netmask)
{
        struct  sockaddr_in     addr;
        struct  ifreq           ifr;
                int                ret = 1;  //set status:   0->Failture 1->Success

        bzero(&addr, sizeof(addr));
        addr.sin_family = PF_INET;
        inet_aton(netmask,&addr.sin_addr);

        strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
        memcpy(&ifr.ifr_netmask,(struct sockaddr *)&addr, sizeof(addr));

        if(ioctl(sockfd, SIOCSIFNETMASK, &ifr) == -1)
                                ret = 0;

        return ret;
}

上层的EJS网页代码(ethernetSet.esp):

<html>
        <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
                <title>Set ethernet IP address</title>
      </head>
       
 
        <body>
        <%if(session['username']){%>           //判断用户是否登录,如果登录就显示该页
       
     <div class="mainTitle marginAuto" id="title">Ethernet IP Setting</div>
        <div class="mainBox marginAuto">
                <div class="mainContent">
                <div class="innerContent">

                                        <form method="POST" action="@@request['SCRIPT_NAME']" >  //提交表单
                                                <table class="inputForm" border=0>
                                                        <tr>
                                                                <td><b>IP Address</b></td>
                                                                <td><input name="ip" type="text" value="@@form['ip']" onBlur="validIP(this,0);"></td>
                                                        </tr>
                                                        <tr>
                                                                <td><b>Netmask</b></td>
                                                                <td><input name="netmask" type="text" value="@@form['netmask']" onBlur="validIP(this,1)"></td>
                                                        </tr>
                                                </table>
                                                <input type="submit"  value="Submit">
                                                <input type="reset" value="Reset">
                                  </form>

                                <%        
                                        if (request["REQUEST_METHOD"] == "POST")   //处理提交的表单
                                        {
                                                var ret;
//form['ip']为表单提交的值,并调用上面写的C代码来设置IP地址,这里底层的C函数能返回MprVar(Object)类型值,或string。
                                                ret = setEthernet(form['ip'], form['netmask']);  
                                                if(0 == ret)  
                                                        write("Set failure: Make sure the input IP address is a valid IPv4 address.</br>");
                                                else
                                                {
                                                        write("The set IP address: " + form['ip'] + "</br>");
                                                        write("The set netmask address: " + form['netmask'] + "</br>");
                                                } 
                                        }
                                %>

                 </div>
         </div>
     </div>

        <%
                }
                else
                {
                        redirect("login.esp");  //如果没登录,就转到login.esp去登录。
                }
        %>               

        </body>
</html>

上面的C++函数需要编译成动态库,然后在appweb.conf里加载这个模块,使这个函数作为appweb的一部分,这样方能使用EJS调用底层的C函数。

在appweb-src-2.4.1\samples\C++\simpleModule 拷贝一份,并更名为ethernetModule,将上面的ethernetModule.cpp 源代码放到该目录下,并将所有文件里的simpleModule改为ethernetModule(注意有大小写)。参考simpleModule改。

然后执行make,如果是交叉编译,他会到找上层Makefile中设置的交叉编译器来进行交叉编译。Make后会得到ethernetModule.so,将其更名为libethernetModule.so并放到appweb指定的动态库路径下,也可以通过LD_LIBRARY_PATH环境变量来指定。

修改appweb.conf (118行左右)为:

LoadModulePath "../lib/arm-atmel-linux/modules"
#这个路径即为appweb寻找动态库的路径,也可以通过LD_LIBRARY_PATH环境变量来指定。

        LoadModule ethernetModule libethernetModule

<if SSL_MODULE>
        LoadModule ssl libsslModule

        <if MATRIXSSL_MODULE>
                LoadModule matrixSsl libmatrixSslModule
        </if>

        <if OPENSSL_MODULE>
                LoadModule openSsl libopenSslModule
        </if>
</if>
重启appweb,这时appweb就会像加载所有其他模块一样(如matrixssl)一样加载我们的ethernetModule.so(可以通过看logs/error.log检查是否加载成功).这样就可以在ethernetSet.esp顺利的调用 ret = setEthernet(form['ip'], form['netmask']);

顺便提一句: appweb和我们的c/c++程序都是运行在同一个进程环境下,这样我们必须保证我们的C/C++代码不能出现任何错误(段错误)导致进程退出。有一次,我就在一行C代码里调用了exit(),结果每导致执行一次该页面,服务器就down掉一次

你可能感兴趣的:(appweb的JavaScript调用C函数的实现过程(以设置eth0 IP地址为例))