第三章 嵌入式Web服务器的实现
3.1 嵌入式Web服务器的概况
随着计算机的日益普及,互联网络(Internet)已经成为人们日常生活的一部分。互联网络是覆盖率极广、联网设备众多、协议完善、功能强大的一种通信方式,已经成为社会重要的基础信息设施,是重要的信息流通渠道。具备互联网络的接入功能,已经成为众多使用微处理器的嵌入式应用的发展方向。网络控制设备逐渐成为一种趋势。
使用互联网络的接入功能将给嵌入式的微处理器应用带来很多好处:利用无所不在的互联网络,可以从全球的任何一个角落实现对设备的监控;利用开放的TCP/IP 网络通信协议,任何的计算机都可以使用通用的网络浏览软件访问设备,不需要专门的计算机和专门的软件;设备的信息通过网页的形式体现。
因此从概念性上来讲,嵌入式Web服务器(Embedded Web Server)是指将Web服务器引入到现场测试和控制设备中,在相应的硬件平台和软件系统的支持下,使传统的测试和控制设备转变为具备了以TCP/IP为底层通信协议,Web技术为核心的基于互联网的网络测试和控制设备。嵌入式Web服务器系统与传统的Web应用相比,简化了系统结构,将信息采集和信息发布都集成到现场的测控设备中。
一般来说,Web服务器通常由以下几个部分组成:
● 服务器初始化部分。这部分主要完成Web服务器的初始化工作,如建立守护进程、创建TCP套接字、绑定端口、将TCP套接字转换成侦听套接字,进入循环结构,等待接收用户浏览器连接。
● 接收客户端请求。由于客户端请求以文本行的方式实现,所以服务器也以文本行为单位接收。
● 解析客户端请求。这部分工作比较复杂,需要解析出请求的方法、URL(统一资源定位符)目标、可选的查询信息及表单信息。如果请求方法为HEAD,则简单地返回响应首部即可;如果方法是GET,则首先返回响应首部,然后将客户端请求的URL 目标文件从服务器磁盘上读出,再发送给客户端:如果是POST,则比较麻烦,首先要调用相应的CGI程序,然后将用户表单信息传给CGI程序,CGI程序根据表单内容完成相应的工作,并将结果数据返回。
● 发送响应信息之后,关闭与客户机的连接。
Linux环境下,主要有三个Web服务器:Httpd、thttpd和Boa。Httpd是最简单的一个Web服务器,它的功能最弱,不支持认证,不支持CGI。thttpd和Boa都支持认证和CGI等,功能都比较全。
如果Web服务器仅需提供一些静态页面,例如系统介绍、简单的在线帮助等,完全可以用静态服务器Httpd 来实现。但若需要与用户进行交互,例如数据查询、实时状态查询等,则必须使用动态Web技术,可以选用thttpd或者Boa来实现。
目前实现动态Web页面有多种技术可供选择,CGI, ASP, PHP等技术。针对具体的嵌入式人机界面要实现动态数据和状态查询,而且要实现逻辑算法的实现,因此选择支持ASP、CGI、embedded JavaScript等脚本语言的Goahead。
3.2Goahead服务器
3.2.1 GoAhead Web服务器的概况
Web服务器采用GoAhead Web服务器,一款主要面向嵌入式系统的Web服务器。它的体积非常小巧,但提供了不少的服务特性,它支持ASP,嵌入的JavaScript与内
存CGI处理。
3.2.2 GoAhead Web服务器的结构
图3-1为GoAhead Web服务器的处理流程,下面简单介绍其结构:
main.c:GoAhead Web服务器的主程序文件,完成初始化、设置环境和启动Web 服务器等功能。
● 网页的根目录:*rootWeb = T("web");修改*rootWeb到新的网页目录下。
● 设置网站主页:websRedirect( wp, T("home.asp"));
● 设置访问密码:*password = T("");
● 设置访问端口:port = 80;
Asp.c:asp解释器模块,主要有以下几个功能函数:
websAspFunctions = symOpen( WEBS_SYM_INIT * 2 );创建asp函数列表,其中WEBS_SYM_INIT在头文件“wsIntrn.h”中定义。
websAspDefine( char_t *name, int (*fn)( int ejid, webs_t wp, int argc, char_t **argv));创建asp函数命令。
websAspFunctions是加载asp函数的标志;
aspOpenCount是使用asp功能块的程序的个数;
int websAspOpen()打开websasp正确返回0;
void websAspClose()关闭asp模块;
int websAspRequest( webs_t wp, char_t *lpath);处理asp请求和script命令。
Form.c:实现/goform的处理,主要有以下几个功能函数:
formSymtab 是/goform处理列表的标志;
int websFormHandler( webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query)处理form请求;
int websFormDefine( char_t *name, void (*fn)( webs_t wp, char_t *path, char_t *query))定义一个form 的函数功能块;
void websFormOpen()打开form表;
void websFormClose()关闭form表。
cgi.c:cgi功能块
int websCgiHandler( webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t* query)处理form请求。
security.c:安全功能块
int websSecurityHandler( webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query)处理安全请求。
基于以上GoAhead Web服务器的结构和特点,针对动态网页的实现方面,采用ASP 和embedded JavaScrip技术来实现,才能实现组态动态网页。
3.3 Goahead服务器的移植
Goahead的移植工作可以分为以下三个步骤来完成:
第一步:下载Goahead源码
由于Goahead的源代码是开放的,因此可以从Goahead的官方网站上下载到Goahead最新版本的源码。最新开放源代码的Goahead版本为2.18。
第二步:配置Goahead Web服务器
解压缩最新版本的Goahead服务器,进入目录,在修改/LINUX下的Makefile文件,将
CC = arm-linux-gcc
AR = arm-linux-ar
加到文件开始处,将cc -c -o $@ $(DEBUG) $(CFLAGS) $(IFLAGS) $<
换成$(CC) -c -o $@ $(DEBUG) $(CFLAGS) $(IFLAGS) $<
第三步:编译并下载
执行
$ Make clean
$ Make
将在LINUX目录下生成webs 和libwebs两个文件,只需将两个文件拷贝到ARM 目标板。
由于嵌入式系统没有足够的内存或存储资源来编译可执行代码,只有通过交叉编译代码来创建针对嵌入式系统的可执行文件,并把此文件送到目标机上执行。因此,
通常的嵌入式系统的开发都是装有Linux的PC机作为宿主机来编译内核和应用程序。编译时可用arm-linux-来指定交叉编译器,不过使用之前先检查PATH路径是否正确。
在ARM目标板上运行Goahead Web服务器,还必须对目标板进行设置,设置如下:首先,在/etc/modules.conf 中加入alias binfmt-464c off ;
其次,由于ARM中的Linux没有hostname,因此设置:hostname localhost; 设置IP地址:ifconfig eth0 192.168.1.34。
这样本地的浏览器或者其他主机的浏览器就可以浏览Goahead Web服务器网页。注意:编译后用 strip 裁减生成的静态库与 webs 的文件大小,两个都在 120kb 左右,否则两个大小有 1.5MB。
3.4 Goahead服务器功能修改与增加
3.4.1 Goahead服务器配置
Goahead服务器得在程序中编译其配置信息,必须在编译之前将系统参数设置好,编译后,便不可以再改变。针对这个功能,可以采取将配置信息存储在文件中,Goahead 运行时去获取相应配置信息即可。那么就不需要重新编译其应用程序,只需要修改一下配置信息。
修改一些参数将其设置成配置信息,使其在修改时,不用重新编译和下载程序,现在做如下规定,配置参数存放在webs文件的同目录下,文件名为:“ServerCfg”。
需要配置的参数主要有以下几个,并定义其顺序,方便程序读取其参数。
0----------*rootWeb = T( "web" )
1----------*password = T( "123456" )
2----------port = 8080 (int)
3----------retries = 5 (int)
4----------umRestore( T("websconfig.txt") )
5----------T( "/goform" )///goform handler
6----------T( "/cgi-bin" )///cgi handler
7----------T( "default.asp" )////default page
8----------T( "asp.asp" )////home page
9----------hostname///
10---------ip地址
定义了初始化程序,在Goahead服务器启动时,首先调用初始化程序;其次才正常启动Goahead服务器。
static int initconfig();//初始化配置信息
为了方便初始化配置数据信息,定义了如下数据结构供初始化函数时使用: #define nmemb 11
struct para_test
{
char name[30];
int size;
}serverCfg[nmemb];
由于Goahead服务器启动过程中需要有hostname和ip地址,因此,在初始化程序中,将设置hostname和ip地址。设置过程中调用system(执行shell命令)系统函数。System()会调用fork()产生子进程,由子进程来调用/bin/sh-c string 来执行参数string字符串所代表的命令,此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD信号会被暂时搁置,SIGINT和SIGQUIT信号则会被忽略。如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。如果string参数为空指针(NULL),则返回非零值。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为system()调用/bin/sh 失败所返回的127,因此最好能再检查errno来确认执行成功。
3.4.2 时间功能模块
为了在人机界面中显示时间,在人机界面做时序调度,方便人机界面做时间轴上的决策,增加了时间功能模块。分成两组:一组为:显示时间;另一组为:获取时间。
显示时间分为三个功能函数,分别为:显示时间、显示日期和显示星期。函数原型如下:
static int aspTime( int eid, webs_t wp, int argc, char_t **argv)时间; static int aspDate( int eid, webs_t wp, int argc, char_t **argv)日期; static int aspWeekDay( int eid,webs_t wp, int argc, char_t **argv)星期;
获取时间分成六个功能函数,分别为:获取小时、分、秒、月、日和星期。函数原型如下:
static int aspGetHour( int eid,webs_t wp, int argc, char_t **argv)小时; static int aspGetMinute( int eid,webs_t wp, int argc, char_t **argv)分; static int aspGetSecond( int eid,webs_t wp, int argc, char_t **argv)秒; static int aspGetMonth( int eid, webs_t wp, int argc, char_t **argv)月; static int aspGetDay( int eid, webs_t wp, int argc, char_t **argv)日; static int aspGetWeekDay( int eid, webs_t wp, int argc, char_t **argv)星期;
为了能在ASP动态网页中调用这些函数,必须将这些功能函数申明成ASP接口函数,如下:
websAspDefine( T("aspTime"), aspTime);
websAspDefine( T("aspDate"), aspDate);
websAspDefine( T("aspWeekDay"), aspWeekDay);
websAspDefine( T("GetHour"), aspGetHour);
websAspDefine( T("GetMinute"), aspGetMinute);
websAspDefine( T("GetSecond"), aspGetSecond);
websAspDefine( T("GetMonth"), aspGetMonth);
websAspDefine( T("GetDay"), aspGetDay);
websAspDefine( T("GetWeekDay"), aspGetWeekDay);
在ASP动态网页中使用时,只需调用相应的函数名称即可使用。返回值为int型的数据,用户可以根据返回的时间,做出基于时间轴的逻辑控制。
3.4.3 显示的功能函数
网页显示有三种主要的元素:文本显示、数值显示和图象显示。针对这三种不同的元素,将ASP动态网页显示分成三种方式来处理如下:
● 文本显示
int websWriteText( int ejid, webs_t wp, int argc, char_t **argv)
{
char_t *text;
ejArgs( argc, argv, T("%s"), &text);
websWrite( wp, T("%S"), text);
return 0;
}
定义成ASP功能函数为:
websAspDefine( T("writeText"), websWriteText);
在ASP动态网页中显示文本信息,只需在脚本语言中写writeText(“文本内容”),就可以将文本信息显示出来。同样可以根据不同情况显示不同的文本信息,这样就可以达到动态效果,而不是静态的网页。
● 数值显示
int websWriteValue( int ejid, webs_t wp, int argc, char_t **argv)
{
char_t *value;
ejArgs( argc, argv, T("%d"), &value);
websWrite( wp, T("%d"), value);
return 0;
}
定义成ASP功能函数为:
websAspDefine( T("writeValue"), websWriteValue);
在ASP动态网页中显示变量值,只需在脚本语言中写writeValue(变量名),就可以将变量数值显示出来。用户可以先根据显示格式,将变量值进行逻辑运算,然后再将其显示出来。
● 图象显示
int websWriteImg( int ejid, webs_t wp, int argc, char_t **argv)
{
char_t *Path;
ejArgs( argc, argv, T("%s"), &Path);
websWrite( wp, T("
"), Path);
return 0;
}
定义成ASP功能函数为:
websAspDefine( T("writeImg"), websWriteImg);
在ASP动态网页中显示图片,只需在脚本语言中写writeImg(“图片路径”),就可以将图片显示出来。使用时,也可以在图片路径后加一些显示参数,也可以根据不同的情况显示不同的图片,这样实现动态网页的效果。
3.4.4 用户跳转实现
由于需要用户认证,因此需要对相应的用户的权限进行设置,因此需要对用户的权限进行设置,针对网页的跳转有以下三种方法:
(1)一种是在网页(.asp)来认证用户的权限,采用GETVAR方式获取从其他网页传过来的数据。采用ejSetResult(eid,T("3"))方式返回数据。
(2)另一种是采用在GOFORM中定义一个功能块,在功能块中做认证用户的权限,再跳转到其他的网页的方式来实现用户认证(浏览器和服务器端处理)。
(3)处理办法和处理首页的方式一样,就是来一个请求,先处理请求,符合要求,跳转到相应的页面。采用websRedirect( wp, T("first.asp"))的方式跳转(服务器端处理)。
为了方便用户认证跳转,采用在GOFORM中定义一个功能块,判断从认证页面传
递过来的用户名和密码,正确与否使用websRedirect跳转到不同的界面。
用户认证的界面如图3-2所示:
要完成页面跳转任务,分以下几个步骤:
首先,在Goahead服务器的主程序中定义一个用来处理用户认证函数formUsercheck,认证流程图3-3如下:
其次,在Goahead服务器启动后,将用户认证函数formUsercheck注册成goform 中的模块,如下:
websFormDefine( T("Usercheck"), formUsercheck);
最后,使用时在需要认证的首页面增加: