本次主要解析STM32网络通信中WebServer应用,从网页界面的编写到浏览器与STM32之间进行通信的数据来说明SSI与CGI的原理及应用,并对GET与POST指令进行应用解析。
硬件和软件环境:
1.硬件环境:STM32F407,网卡芯片LAN8720,其他部分参考正点原子的407探索者开发板。
2.软件环境:keil5,LWIP1.4.1,主要是基于正点原子STM32F407探索者的第六十章网络通信实验程序。
一、程序流程解析
为了方便查看浏览器与STM32之间的数据通信,建议程序中使用固定IP的方式,如192.168.1.101,建议使用软件Wireshark来查看网络数据。
首先是打开Wireshark,选择本地连接
然后在地址栏输入 ip.addr == 192.168.1.101,然后按右边的箭头开始接收数据
这时在浏览器输入IP地址(如192.168.1.101)就可以看到如下数据:
这时浏览器给STM32发出GET指令数据,而STM32通过函数http_recv()接收数据,并把接收到的数据进行解析、处理,然后把指令要求的数据发给浏览器,这时浏览器上面就会显示相应的网页界面。具体在函数http_recv()里面的执行流程如下:
http_recv()----->判断收到的是有效数据后,调用函数http_parse_request()----->解析是GET指令还是POST指令(输入IP后下发的是GET指令),如果是GET指令,则直接是调用函数http_find_file()----->判断指令的内容是请求打开默认的根文件(如打开index.shtml或test.shtml)还是CGI程序指令(CGI指令主要就是在网页界面上按下按钮等下发下来的一系列相关操作指令,后面再对此解说)----->如指令为求打开默认的根文件,则打开存放在SPI FLASH芯片W25Q128或者SD卡中的SHTML文件,并获取相应的数据----->然后通过函数http_init_file()对数据进行初始化,然后退出函数http_find_file(),再退出函数http_parse_request()----->然后运行到函数http_send_data(),查找SSI的Tag,找到之后把相应的内容添加进入,然后把数据发回给浏览器----->浏览器显示对应的网页界面。
打开一个网页的程序流程大概就是这样。浏览器与STM32之间的网络通信数据,简单理解就是互相发送一串串字符串,而一帧数据字符串里面包含了某些固定的字符串(或字符,比如”GETHTTP/1.1”、”?”、”&”),这些字符有特定的含义,而我们需要在这些字符串中找出几个特殊字符,然后根据含义进行解析处理。
二、SSI的原理及应用解析
首先来了解一下SSI的原理:将内容发送到浏览器之前,可以使用“服务器端包含 (SSI)”指令将文本、图形或应用程序信息包含到网页中。例如,可以使用 SSI 包含时间/日期戳、版权声明或供客户填写并返回的表单。对于在多个文件中重复出现的文本或图形,使用包含文件是一种简便的方法。将内容存入一个包含文件中即可,而不必将内容输入所有文件。通过一个非常简单的语句即可调用包含文件,此语句指示 Web 服务器将内容插入适当网页。而且,使用包含文件时,对内容的所有更改只需在一个地方就能完成。
因为包含 SSI 指令的文件要求特殊处理,所以必须为所有 SSI 文件赋予 SSI文件扩展名。默认扩展名是 .stm、.shtm 和 .shtml。
以上内容来自百度,简单的理解就是在把网页界面程序的SHTML文件发给浏览器之前,通过某几个SSI的主要函数把SHTML里面的数据进行了替换,可以说是增加了某些程序进去。而替换的规则就是查找到,这个XXX是可以自己定义的,比如我定义LWIP_HTTPD_MAX_TAG_NAME_LEN为3,那就是这可以放的是3个字符,比如,然后找到这个后,我把某个程序加上去,比如加上”测试ADC”。这样可能不好理解,还是直接上HTML程序,首先看一个简单的SHTML程序:
test
把这个程序复制到文本文档里面,然后另存为一个index.shtml,然后用浏览器打开就可以看到网页界面上显示:test
这个是没有经过SSI处理的,经过处理之后的SHTML程序为:
test测试ADC
用浏览器打开就可以看到网页界面上显示:test测试ADC
这样应该就能够理解了,找到指定的字符串后,是在后面添加程序,至于把这个放在哪里,就看怎么去编写这个SHTML程序了。
SSI的原理其实不难理解,主要在httpdi_cgi_ss.c里面,实际使用到的有:
static constchar *ppcTAGs[]= //SSI的Tag
{
"t", //ADC值
"w", //温度值
"h", //时间
"y", //日期
"adc",//测试ADC
};
这个数组就是存放SSI的Tag数组,在打开SHTML文件时,会通过函数get_tag_insert()把SHTML里面的Tag给找出来并且添加指定的内容,而添加的内容是通过函数SSIHandler()来判断,然后运行指定的函数去添加,而这个指定的函数,比如:
void ADC_Handler(char*pcInsert)
{
sprintf(pcInsert,”测试ADC”);
}
那么就可以直接达到在网页界面上的”test”后面添加”测试ADC”这样的效果。
但是,有个地方要注意了,如果添加的内容比较大,那么就要修改
#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 的大小,这个就根据自己的需求来更改了,如果需要增加的内容比较多,改成1024甚至更大都可以。如果比实际的内容小了,那会导致网页界面无法正确显示,严重的会引起STM32硬件错误中断。
可能有些人对这个SHTML的数据(或文件)存放在哪里不是很理解,先说一下目前用的一个方法,就是做好成网页文件之后,用makefsdata.exe来生成数组,然后存放在程序里,在程序里再调用。这个方法不建议用,一是不直观,第二是修改起来太麻烦,第三是占用资源。不过有个方法和该方法类似,但方便很多,适用于HTML代码比较少,界面比较简单的网页界面。比如:
voidhttp_write_testweb(char *pbuff, int *ppos)
{
*ppos += sprintf(pbuff + *ppos," ");
*ppos += sprintf(pbuff + *ppos,"");
*ppos += sprintf(pbuff + *ppos,"\r\n");
*ppos += sprintf(pbuff + *ppos,"
}
该函数直接把一个网页界面的HTML代码存入pbuff数组中,代码的长度是ppos,通过调用这个函数,把pbuff数组的内容通过函数get_tag_insert()添加Tag的内容后再发给浏览器,在浏览器上面就能看到如下的界面:
另一种方法就是原子哥在第六十四章综合实例用的,就是把SHTML文件存放在SPI FLASH 芯片W25Q128中,然后通过FATFS文件系统直接打开调用SHTML文件,目前定义的路径是:#define HTTP_SRC_PATH "1:SYSTEM/LWIP/WebServer",不过在网页界面调试阶段,建议把路径改成SD卡下,那样不用每次修改SHTML文件后总是去把文件烧进W25Q128中,直接把修改好的文件放到SD卡下就可以。对于制作比较复杂的界面,建议用这种方法,很直观,而且懂得编写HTML的话,界面很快就能做出来。
三、CGI的原理及应用解析
CGI是外部应用程序(CGI程序)与WEB服务器之间的接口标准,是在CGI程序和Web服务器之间传递信息的过程。CGI规范允许Web服务器执行外部程序,并将它们的输出发送给Web浏览器,CGI将Web的一组简单的静态超媒体文档变成一个完整的新的交互式媒体。
说白了,STM32有了CGI处理程序之后就能和网页产生互动,比如一个用户登陆界面:
输入正确的用户名和密码后,点击<登陆>按钮,这时浏览器就下发一个指令数据,该指令数据里面就包含了需要处理的用户名和密码数据(输入框里面的数据,比如用户名是admin),STM32接收到该指令数据之后,经过解析处理,然后把处理之后的数据发回浏览器,比如验证输入的用户名和密码为错误,则在浏览器上弹出一个串口提示“用户名或密码错误!”,如果输入的用户名和密码为正确,则在浏览器上直接跳转进入另外一个网页界面。
为了比较方便理解,先看看这个登陆界面的HTML代码:
functiondoLoad(){
}
这个代码中的