嵌入式web服务器在物联网、智能家居、以及安防系统方面上的应用越来广泛,因为对这方面感兴趣,近一段时间尝试了下,搭建一个最基本的轻巧小型嵌入式web服务器。
1 环境搭建需要
2 BOA的编译(结合网上整理)
3 BOA的配置
4 BOA的移植
5 静态网页测试
6.CGIC库的移植及使用(留言板小实例)
1 环境搭建需要
Ubuntu:16.04 ,使用其他版本也可以,不同之处在于你自己的交叉编译链。
目标板:s3c2440开发板
编译工具:在Ubuntu上如果用的是arm-linux-gcc -4.3.2编译工具链编译BOA,则目标板中的跟文件系统必须是用此版本的编译工具制作的,在这一点新手特别容易忽略,导致完成移植以后不能启动BOA服务器。
2 BOA的编译(结合网上整理)
2.1 先去官网下载boa源码:下载地址: http://www.boa.org/ 或者去本人下载页下载boa。(下载页中还有cgic源码包)
将下载好的boa源码复制到Ubuntu上,在命令行输入:tar xzf boa-0.94.13.tar.gz 解压源码包。
2.2 编译安装boa所需要的工具bison,flex
在命令行输入:sudo apt-get install bison flex
2.3 修改文件
有些文件不修改,会导致编译错误,不需要知道为什么,我们的重点也不在这。
(1)修改 src/compat.h
找到
#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
修改成
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff
(2)修改 src/log.c
注释掉
if (dup2(error_log, STDERR_FILENO) == -1) {
DIE("unable to dup2 the error log");
}
为:
/*if (dup2(error_log, STDERR_FILENO) == -1) {
DIE("unable to dup2 the error log");
}*/
(3)修改src/boa.c
注释掉下面两句话:
if (passwdbuf == NULL) {
DIE(”getpwuid”);
}
if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
DIE(”initgroups”);
}
为
#if 0
if (passwdbuf == NULL) {
DIE(”getpwuid”);
}
if (initgroups(passwdbuf->pw_name, passwdbuf->pw_gid) == -1) {
DIE(”initgroups”);
}
#endif
注释掉下面语句:
if (setuid(0) != -1) {
DIE(”icky Linux kernel bug!”);
}
为
#if 0
if (setuid(0) != -1) {
DIE(”icky Linux kernel bug!”);
}
#endif
2.4 生成Makfile
在命令行输入以下命令:
cd boa-0.94.13/src
./configure
2.5 修改Makefile
vi src/Mkaefile 打开文件
修改CC = gcc 为 CC = arm-linux-gcc
修改CPP = gcc -E 为 CC = arm-linux-gcc -E
2.6 编译
在命令行输入以下命令:make
编译完以后查看boa的信息:ls -l boa
出现: -rwxr-xr-x 1 david david 189223 2009-05-31 13:44 boa
2.7 在ARM根文件系统下创建目录(在开发板上进行)
因为需要把boa等文件移植到开发板的根文件系统中,所以在开发板的跟文件下建立一些目录是必要的。一般根文件系统都有var这个目录,如果没有就创建一个var(命令:mkdir var)目录。
如果有var目录,就进入var目录(命令:cd var),在var目录下建立新目录www(命令:mkdir www)。在此目录需要放xx.html文件。
在var目录下还需要创建一个boa日志文件:命令:mkdir log
mkdir boa (mkdir是创建文件夹,touch是创建文件)
touch access_log
touch error_log
在www目录下还得创建一个文件夹:cgi-bin,用来放xx.cgi。命令:mkdir cgi-bin
在etc文件夹中创建一个boa目录,用来放boa的配置文件boa.conf。
3 BOA的配置
这一步也是在Ubuntu上完成。
在boa解压的源码中,在其顶层目录中有一个boa.conf示例,在后面会将其修改好的boa.conf复制到开发板的根文件系统中。
命令行: vi boa.conf
(1)修改端口
Port 80,在boa.conf中的端口号是80,此端口为默认端口,比建议修改。如果修改为8080,则在服务器搭建好以后,在打开网页输入网址时需要把端口号也写上,例如:http://192.168.1.17:8080/XXXX,如果不修改则这样输入:http://192.168.1.17/XXXX。
(2)Group的修改
修改 Group nogroup
为 Group 0
(3)user的修改
修改 User nobody
为 User 0
(4)AccessLog修改
如果boa.conf文件中的这两项跟下面一样,则不用修改,否则修改成如下所示。
AccessLog /var/log/boa/access_log
AccessLog /var/log/boa/error_log
(5)DocumentRoot 的修改
如果boa.conf文件中的此项跟下面一样,则不用修改,否则修改成如下所示。
DocumentRoot /var/www
(6)ScriptAlias的修改
修改ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
为 ScriptAlias /cgi-bin/ /var/www/cgi-bin/
(7)ServerName的设置
修改#ServerName www.your.org.here
为 ServerName www.your.org.here
4 BOA的移植
进行移植就需要Ubuntu与开发板进行通信了,本人是用nfs挂载文件传输文件的。挂载的目录为 work/nfs_root
4.1 移植boa和其配置文件boa.conf
在2.7中我们已经把所有需要的文件夹和文件都创建完毕,所以直接进行复制。我们把编译生成的boa文件,和boa的配置文件boa.conf放到nfs挂载的目录下。
把文件boa复制到根文件系统下的bin目录下,先进入挂载目录下:cd /mnt,然后执行:cp boa /bin。
把boa.conf文件复制到根文件系统下的 etc/boa下:cp boa.conf /etc/boa
4.2 移植系统配置文件mime.types
将ubuntu下/etc/mime.types拷贝到开发板根文件系统的/etc下
先将mime.types文件复制到挂载目录下,然后执行:cp mime.types /etc。
将所有文件移植完毕后进入bin目录下,启动boa服务器,执行:./boa ,会出现如下信息:
[01/Jan/1970:00:00:57 +0000] boa: server version Boa/0.94.13
[01/Jan/1970:00:00:57 +0000] boa: server built Oct 21 2018 at 00:29:23.
[01/Jan/1970:00:00:57 +0000] boa: starting server pid=881, port 80
说明boa启动成功。
至此,BOA的配置以及移植已经完成。下面让我们试下静态网页的效果吧。
5 静态网页测试
我们先写一个简单的xx.html网页放到根文件系统目录下的 var/www中。
hello.html内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> 我的第一个网页 </title>
<BR>
<div align="center" class="STYLE5"><BR>
<span class="STYLE2"> Hello </span><BR>
<BR>
<BR>
<BR>
<BR>
<span class="STYLE2"><a href="https://blog.csdn.net/qq_30592303"> 欢迎访问本人博客</a> </span><BR>
<BR>
</div>
</head>
</html>
然后保存为.html的网页文本,放入到根文件系统下的var/www中。
然后在的电脑上网页的网址栏中输入:http://192.168.1.17/hello.html ,看下效果:
如果出现以上效果,说明boa服务器搭建成功。
说明:一定要注意上述图片的画红线处,跟平常的网址不太一样,是http而不是https。
能使网页出现效果的前提条件之一是:开发板的ip必须与你的电脑IP ping通。
6.CGIC库的移植及使用
6.1 cgic源码的移植
从CGIC的主站点http://www.boutell.com/cgic/下载源码或者从本人博客下载页中下载CGIC和BOA 的源码包。
将其在Ubuntu的环境下解压并进入,执行如下命令:
tar xzf cgic207.tar.gz
cd cgic207
修改Makefile文件:
(1)找到CC=gcc
AR=ar
RANLIB=ranlib
修改为CC=arm-linux-gcc
AR=arm-linux-ar
RANLIB=arm-linux-ranlib
(2)找到 cgictest.cgi: cgictest.o libcgic.a
gcc cgictest.o -o cgictest.cgi ${LIBS}
capture: capture.o libcgic.a
gcc capture.o -o capture ${LIBS}
修改为 cgictest.cgi: cgictest.o libcgic.a
arm-linux-gcc -g cgictest.o -o cgictest.cgi ${LIBS}
capture: capture.o libcgic.a
arm-linux-gcc -g capture.o -o capture ${LIBS}
我看网上还有其他改法,但是cgic207版本的我只能这么改才能编译通过。
修改完Makefile以后就可以编译了,执行:make
会生成CGIC库libcgic.a,我们通过调试辅助程序capture和测试程序cgictest.cgi,来验证生成CGIC库的正确性。
根据移植boa的方法我们把capture,cgictest.cgi这三个文件移植到根文件系统下的var/www/cgi-bin下面。
然后在电脑的网页的网址栏输入:http://192.168.1.17/cgi-bin/cgictest.cgi,如果出现一个正常网页说明,CGIC库已经移植成功。
说明:一定要注意上述图片的画红线处,跟平常的网址不太一样,是http而不是https。
能使网页出现效果的前提条件之一是:开发板的ip必须与你的电脑IP ping通。
6.2 开发板与网页互交实例(留言板功能设计)
1 )HTML文件与CGI文件之间如何调用
在 HTML中,表单 (FORM)是最主要的传递信息的手段,它适用于任何浏览器。表单中有很多元素,包括输入文本框,单选框,多选框,按钮,等等,可以提供信息的交互。表单数据一般是以POST方法提交给服务器,由CGI程序获得,程序根据元素名字/值中的元素名字来区分数据,完成数据处理后,再读取相应的模板文件,根据注释标记将对应的数据填充到HTML文本中去,生成最后的页面返回给浏览器。
在HTML文件中,一般都有<form name="form1" action="/cgi-bin/xxx.cgi" method="POST"> 类似这样的语句,在表单中的action表示的是在HTML文件中的某个动作完成后,以POST的形式将HTML内容提交给服务器,经过解析后, 将/cgi-bin目录下的xxx.cgi结果返回浏览器界面。
2 )编译xx.cgi文件
在Ubuntu的环境下,执行命令:arm-linux-gcc - o xx.cgi xx.c
本实例中,需要编译两个C文件,liuyan.c 和 chakan.c ,C源码在下面。
编译liuyan.c执行:arm-linux-gcc -o liuyan.cgi liuyan.c
编译chakan.c执行:arm-linux-gcc -o chakan.cgi chakan.c
例如:
<form ACTION="/cgi-bin/liuyan.cgi" METHOD="POST">
<P>请输入您的留言(最多80个字符):<BR><INPUT NAME="data" SIZE="60" MAXLENGTH="80"><BR>
<INPUT TYPE="SUBMIT" value="确定">
此程序中有一句<INPUT TYPE="SUBMIT" value="确定"> ,当在HTML界面点击确定按钮后,经过解析后,执行ACTION="/cgi-bin/liuyan.cgi"这句话,从而返回liuyan.cgi程序的输出结果。
3 )功能介绍
输入网页弹出前台界面,提示用户输入留言,留言提交后可以查看同时也可以查看信息。留言提交信息可以留言信息,但是查看留言信息只能留言字母、数字和符号,中文会乱码。水平有限,有需要的可以参考,望轻喷。
4) 源代码详情
(1)HTML代码 liuyan.html
<html>
<head>
<meta charset="utf-8">
<title>留言本</title>
</head>
<form ACTION="/cgi-bin/liuyan.cgi" METHOD="POST">
<P>请输入您的留言(最多80个字符):<BR><INPUT NAME="data" SIZE="60" MAXLENGTH="80"><BR>
<INPUT TYPE="SUBMIT" value="确定">
</form>
<form ACTION="/cgi-bin/chakan.cgi">
<P><INPUT TYPE="SUBMIT" value="查看">
</form>
</html>
此部分代码可以分为两部分来调用cgi程序。
第一部分:
<form ACTION="/cgi-bin/liuyan.cgi" METHOD="POST">
<P>请输入您的留言(最多80个字符):<BR><INPUT NAME="data" SIZE="60" MAXLENGTH="80"><BR>
<INPUT TYPE="SUBMIT" value="确定">
</form>
通过POST方式对表单“确定”提交的时候,调用“/cgi-bin/liuyan.cgi”。
第二部分:
<form ACTION="/cgi-bin/chakan.cgi">
<P><INPUT TYPE="SUBMIT" value="查看">
</form>
通过POST方式对表单"查看"提交的时候,调用“/cgi-bin/chakan.cgi”。
代码效果如下图所示:
(2)以下是留言板写入源代码:liuyan.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXLEN 80
#define EXTRA 5
/* 4个字节留给字段的名字"data", 1个字节留给"=" */
#define MAXINPUT MAXLEN+EXTRA+2
/* 1个字节留给换行符,还有一个留给后面的NULL */
/* 要被添加数据的文件 */
#define DATAFILE "data.txt" //文本路径,/var/www/cgi-bin/data.txt
void unencode(char *src, char *last, char *dest)
{
for(; src != last; src++, dest++)
if(*src == '+')
*dest = ' ';
else if(*src == '%') {
int code;
if(sscanf(src+1, "%2x", &code) != 1) code = '?';
*dest = code;
src +=2; }
else
*dest = *src;
*dest = ' ';
*++dest = NULL;
}
int main(void)
{
char *lenstr;
char input[MAXINPUT], data[MAXINPUT];
long len;
printf("Content-Type:text/plain;charset=utf-8 \n\n");//
lenstr = getenv("CONTENT_LENGTH"); //获取环境变量内容(CONTENT_LENGTH为环境变量的名称)
//如果长度为零或者输入过长
if(lenstr == NULL || sscanf(lenstr,"%ld",&len)!=1 || len > MAXLEN)
printf("表单提交错误");
else {
FILE *f;
fgets(input, len+1, stdin); //读取一串数据
unencode(input+EXTRA, input+len, data);
f = fopen(DATAFILE, "a"); //打开文本
if(f == NULL)
printf("对不起,意外错误,不能够保存你的数据 ");
else
fputs(data, f);//向指定文件写入字符串
fclose(f);
printf("非常感谢 ,您的数据已经被保存\n信息为:%s",data);
}
return 0;
}
写入信息效果图:
在下图中HTML文件在提交表单后,调用/cgi-bin目录下的liuyan.cgi输出结果
(3)留言完毕后查看留言信息代码:chakan.c
#include <stdio.h>
#include <stdlib.h>
#define DATAFILE "data.txt" //文本路径,/var/www/cgi-bin/data.txt
int main(void)
{
FILE *f = fopen(DATAFILE,"r");//可读模式打开文本
int ch;
if(f == NULL) {
printf("Content-Type:text/html;charset=utf-8 \n\n");
printf("意外错误,无法打开文件"); }
else {
printf("Content-Type:text/html \n\n"); //文本形式打印
while((ch=getc(f)) != EOF)
putchar(ch); //通过循环打印每个字符
fclose(f); }
return 0;
}
点击查看的按钮后,输出结果图:
以上几个效果图都算是前台的效果,我们现在看下硬件后台开发板的跟文件系统的目录下是否有我们写入的信息。
从以上图看出,我们写入的信息存在了后台。
说明:在liyuan.c的程序中,有一个“#define DATAFILE "data.txt"”,这个存放信息的data.txt新建在根文件系统下的/var/www/cgi-bin/目录下。如果在完全没有错误的情况下,在HTML文件中提交“确定”的时候,没有调用liuyan.cgi程序,就在开发板上根文件系统的最顶层创建data.txt文件。
实例来源:https://blog.csdn.net/dreaming_terry/article/details/53492290
对代码有所修改,网页处理界面大致相同,区别:原文章中分为后台和前台,后台为软件服务器。本文章是嵌入式web服务器,后台是硬件开发板,通过CGI公共网关接口实现开发板与前台网页的互交。
如有错误望能指出,一起进步!!!