Android搭建WEB Server—boa(二)

来源:互联网 发布:js省市区三级联动插件 编辑:程序博客网 时间:2024/05/01 11:34

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_ROOT30 #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 0