Android搭建WEB Server—boa(二)

Android搭建WEB Server—boa(二)


2017/2/17 14:03:43

上一篇只是对于移植boa的基本讲解,在移植过程中,会出现很多问题。上文已经说明了如何去修改源码和boa.conf文件,而使我们通过编译。目前,我们要做的事情是:执行CGI程序。这个CGI程序有点特殊,它要调用system()函数,来运行一个脚本文件。这就要求它所需要的执行权限必须为root,因为如果不是root用户,调用system()执行脚本是不通过的。即使这个脚本文件的权限为777,仍旧会提示Permission denied。

1 CGI程序


CGI(Common Gateway Interface)公共网关接口。这里不详细介绍CGI,有兴趣的可以搜索一下CGI和fastCGI,资料很多,不怕找不到。推荐一个CGI的资料,很详细,写的很好。http://www.jdon.com/idea/cgi.html
简单说下我对CGI的理解:CGI不能算一段程序,在B/S架构中,它运行在Server端,接收来自WEB服务器的数据,将处理结果返还给WEB服务器。它可以使用任何一种语言开发,在嵌入式领域,C/C++无疑是最快的、最合适的。下面是我的CGI程序。

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int main()
{
    char msg[100];
    memset(msg,0,100);
    fgets(msg,sizeof(msg),stdin);
    system("sh ./test.sh"); //test.sh仅仅是用来传建test.txt文件的脚本。

    puts("<Content-type:text/html>\n");
    puts("<html>");
    puts("<head>");
    puts("<title>test.cgi</title>");
    puts("</head>");

    puts("<body>");
    printf("%s\n",msg);
    puts("</body>");

    puts("</html>");
return 0;
}

2 HTML主页


上文中需要一个index.html文件,充当主页。本文要比之前高深一点,但实际上,稍微懂点html知识,就能写一个简单的交互页面。主要是需要与CGI程序进行数据的交接。下面是我的index.html。

<Content-type:text/html>

<html>
<head>
    <meta charset="utf-8">
    <title>Login</title>
</head>

<body>
    <h1 align="center">Login</h1>
    <hr><br>

    <form action="http://192.168.43.1/cgi-bin/test.cgi" method="POST">
    目的IP<input type="text" name="dstip"><br>
    目的端口<input type="text" name="dstport"><br>
    摄像机IP<input type="text" name="cameraip"><br>
    <input type="submit" value="提交">
    </form>

</body>
</html>

3 调试


问题1 不能执行shell脚本

首先,进入Android系统shell。切换以root用户登录shell。在shell中启动boa服务进程。用浏览器访问时,可以显示主页index.html,但是cgi程序并不能顺利执行下去。上述中cgi程序调用了system()去执行test.sh脚本,创建test.txt文件。查看etc/boa/cgi-bin/目录可知,并没有生成test.txt。
搜索了网上的一些文章,大多与我的情况相悖。我在boa.conf中配置的是没有问题的,可以查看一下我上篇文章,这里就不加赘述了。最后才明白,出现这样的错误是因为Permission denied。Android系统中,我在shell里切换了root身份登录启动的boa,其实并没有真的以root身份去运行boa这个进程。导致了我不能在cgi程序中执行脚本。请教了前辈,才发现这个问题的症结所在。惭愧……
解决的方法:在init.rc中以root身份启动boa。

问题2 boa开机不能启动

首先来说说init.rc。init.rc是管理Android系统开启启动项的文件,init.rc是必须要在源码中更改的,它被做成ramdisk.img的一部分。更改init.rc源码,要重新用 mmm 命令编译,并重新刷ramdisk.img。详细的介绍请自行百度,不做赘述。我在init.rc文件追加(记住别tm写在第一行,不靠谱!!!)了boa的启动代码。

......
/* 开机启动boa */
service boa /system/bin/boa
    class main
    user root
......

编译后,刷入ramdisk.img,修改权限等等。根据前辈建议,把etc/boa更换了路径,改成/data/boa。相应的boa.conf文件中也更改一下配置路径。弄好之后,本以为万事大吉,搞定收工。可惜,bug总是不想我按时吃饭哪。
ps看了一下进程,发现boa压根就没启动。想着是启动出错了,于是更改了一下init.rc,调试调试。

......
service boa /system/bin/logwrapper /system/bin/boa
    class mian
    user root
......

这条代码是为了调试boa,他会将在启动过程中出现的log输出到logcat.log当中。
再次重启后,导出logcat.log,发现boa报错。

boa : Could not chdir to “etc/boa”:aborting
boa : boa terminated by exit(1)

原因很简单,上面我将boa的目录转到了data下,特别是boa.conf文件不在etc/boa/下,而是在data/boa/下。这就得改boa源码了。在boa/src/下,修改defines.h。

......
29 #ifndef SERVER_ROOT
30 #define SERVER_ROOT "/etc/boa" —> "/data/boa"
31 #endif
......

交叉编译后,再次运行。嗯,至少不报前面的错误了。

问题3 boa启动异常

之前解决了boa不能启动,没想到boa启动异常了。具体的表现呢,就是logcat.log中没有boa的错误输出,但是boa的error_log中每隔五秒会写入一次启动的信息,包括进程号,端口等。进程号一直增大,端口号一直不变。ps查看时,却找不到boa的进程。初步判断是boa在init过程中出现了错误。boa处于启动过程,但可能系统认为boa已经退出了,所以又重新启动了boa,所以导致反复重启,却看不到进程。
这时,adb shell后,手动启动boa,ps看启动成功了,但是,error_log中有爆出了新错误。

boa : boa.c:194 - unable to bind: Address already in use
boa : boa.c:194 - unable to bind: Address already in use
boa : boa.c:194 - unable to bind: Address already in use

这玩意每隔5s出来一次,没什么特殊含义,就是端口复用了。可就是这端口复用,困扰了我两天。按照网上的说法,Android却是存在这种机制,端口可能要等待一段时间才能重新bind。使用setsockpot()函数可以实现端口的复用。听起来很简单哈,可惜,查看代码中却是已经使用了这个函数,仍然出现这个报错。
然后,更改了init.rc的内容如下:

......
service boa /system/bin/boa
    class main
    user root
    oneshot  //表明此进程仅仅启动一次,即使失败也不会重启
......

万万没想到,这样一来竟然丝毫没出现问题。boa开机正常启动,cgi程序正常运行,error_log也没有再报错。看到这里,估计有些人已经明白boa错误的原因了。原因就是,boa在init过程中确实启动了,但是出于某种原因,一号进程认为这个boa已经退出了,我要重新启动它,所以造成了不断地重启。这时adb shell来启动boa,就相当于有两个进程都是boa,都在bind同一个端口,造成了端口被占用的报错。
清楚了原因后,只能去查看源码了。在源码中有这么一段:

......
    /* background ourself */
if (do_fork) {
        switch(fork()) {
        case -1:
            /* error */
            perror("fork");
            exit(1);
            break;
        case 0:
            /* child, success */
            break;
        default:
           /* parent, success */
            exit(0);
            break;
        }
    }
......

这段代码的意思呢就是fork()了一个子线程去执行下面的,而父线程直接退出了。虽然我也不知道为什么作者会加入这么一段代码,(时间问题,不深究,以后再说)但我想,这就是造成上面报错的根本原因。在boa的init过程中,boa父线程退出时给一号进程发送了一个信号,它退出的信号。于是一号进程认为boa退出了,所以重新又起了一次boa,如此周而复始。分析之后,将这段代码注释掉即可。同时将oneshot去掉。我可以直接用父进程完成接下来的任务,而不需要fork()子线程。
至此,圆满结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值