历史上的今天
返回首页

历史上的今天

今天是:2025年06月29日(星期日)

2020年06月29日 | STM32 网络通信Web Server中 SSI与CGI的应用解析

2020-06-29 来源:eefocus

本次主要解析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,"rn");

      *ppos += sprintf(pbuff + *ppos,"

");

      *ppos += sprintf(pbuff + *ppos,"test");

      *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(){

}

用户名

密码


 

这个代码中的

表明,浏览器是下发POST指令,其中包含了checklogin.cgi处理,而这个checklogin.cgi处理是要STM32在程序里面处理。直接看点击<登陆>之后浏览器下发给STM32的数据:

 

 

从数据中可以看到”POST / checklogin.cgi”这个字符串,这个就是一个POST命令,在函数http_parse_request()中解析出来。而在后面的数据中有”username=admin&password=admin&login=%B5%C7%C2%BD”这一串数据就是要在CGI处理函数Chacklogin_CGI_Handler()中进行解析和处理,并把处理的结果发回给浏览器。


在httpdi_cgi_ss.c里面有:

static consttCGI ppcURLs[]= //cgi程序

{

      {"/checklogin.cgi",Chacklogin_CGI_Handler},

};

//CGI 用户和密码检测设置 控制句柄

const char*Chacklogin_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char*pcValue[])

{

  u8 i=0; //注意根据自己的参数的多少来选择i值范围

      u8 passchack=0;

      iIndex =FindCGIParameter("username",pcParam,iNumParams);  //找到bktime的索引号

      if(iIndex != -1) //找到pagingvol索引号

      {

           for(i = 0;i < iNumParams;i++)

           {

                 if(strcmp(pcParam,"username")== 0)  //查找CGI参数

                 {

                      if(strcmp(pcValue,"admin")== 0)//用户名正确

                      {

                            passchack++;

                      }

                 }

                 elseif(strcmp(pcParam,"password") == 0)  //查找CGI参数

                 {

                      if(strcmp(pcValue,"admin")== 0)//密码正确

                      {

                            passchack++;

                      }

                 }

           }

           if(passchack > 1)//用户名和密码都正确了

           {

 

                 return "/index.shtml";     //把保存成功的信息以及修改后的信息重新上传

           }

      }

 

      return "/error.shtml";      //把保存成功的信息以及修改后的信息重新上传

}

函数Chacklogin_CGI_Handler()就是验证用户名和密码是否是”admin”,正确则把index.shtml这个SHTML文件发给浏览器,浏览器则显示index.shtml的网页界面;如果用户名和密码错误则返回错误的网页界面(error.shtml的界面)。

 

  关于CGI处理函数是在函数http_find_file()中解析到"/checklogin.cgi"后被调用,而在浏览器下发的指令中,有GET指令和POST指令的区别,这两个指令都能够带有"/checklogin.cgi",但STM32对这两个指令的解析方法是不一样的,这一点是要注意去区分,相对来说目前的程序是直接用的GET指令来下发表单数据(就是带有代码的网页界面),这个指令下发下来可以直接被解析出来并调用CGI处理函数,但它传下来的参数会直接显示在浏览器的地址输入栏里,比如“username=admin&password=admin&login=%B5%C7%C2%BD”,这样就不是特别的安全,所以不建议用次方法下发表单数据。而用POST指令下发则不会这样,相对来讲会更安全,而且如果是下发一个文件的数据,比如做网络升级时下发的bin文件,这个时候就是得用POST指令。所以接下来重点介绍POST指令的应用。

推荐阅读

史海拾趣

申风(everanalog)公司的发展小趣事

在申风(everanalog)公司的发展过程中,知识产权保护和品牌建设一直是其重点关注的问题。公司注重技术研发和知识产权保护,已经拥有多项专利和注册商标。这些知识产权的保护不仅提升了公司的核心竞争力,也为公司的品牌建设提供了有力支持。同时,公司还积极参与行业展会和交流活动,提升品牌知名度和影响力。

骏晔科技(DreamLNK)公司的发展小趣事

随着公司业务的不断发展和市场规模的扩大,骏晔科技开始积极布局全球市场。公司通过与全球数十家半导体企业建立战略合作关系,拓展海外市场。同时,公司还积极参加国际电子展会和论坛,与全球同行交流合作,共同推动电子行业的发展。通过这些努力,骏晔科技在国际市场上逐渐崭露头角,成为了备受关注的行业新星。

Amphenol(安费诺)公司的发展小趣事

随着蓝牙技术的普及和市场的快速发展,骏晔科技敏锐地捕捉到了这一趋势。公司投入大量研发资源,成功推出了高性能的CC2340蓝牙模块。这款模块以其出色的性能和稳定性,赢得了市场的广泛认可。同时,骏晔科技还针对IoT远距离通信技术,推出了ChirpLAN™网关套件,进一步巩固了其在蓝牙市场的地位。

ABI Electronics公司的发展小趣事

ABI Electronics公司的起点可以追溯到其对电路板测试技术的深入研究。在创立初期,ABI便以开发出高精度、高效率的电路板故障检测仪为目标。通过对电路板测试技术的不断钻研和创新,ABI成功推出了一系列性能卓越的测试设备,这些设备不仅提高了电路板测试的准确性和效率,也极大地降低了测试成本,赢得了市场的广泛认可。

EDAC公司的发展小趣事

面对数字化浪潮的冲击,ECS-D公司积极拥抱数字化转型,通过引入先进的信息技术和智能化设备,提升企业的运营效率和创新能力。公司建立了数字化管理平台,实现了生产、销售、财务等各个环节的信息化管理。同时,ECS-D公司还加强了对员工的数字化培训和教育,提升员工的数字化素养和创新能力。这些数字化转型的举措使ECS-D公司在激烈的市场竞争中保持了领先地位,也为公司的未来发展奠定了坚实的基础。

以上五个故事均以ECS公司的发展为主题,分别从不同角度描述了这些公司在电子行业中的成长历程和成功经验。这些故事旨在展示ECS公司如何通过技术创新、全球化战略、品质管理、绿色环保理念和数字化转型等方式实现持续发展和壮大。

Data Delay Devices公司的发展小趣事

在竞争激烈的电子行业中,质量管理是企业生存和发展的关键。DDD公司高度重视产品质量管理,建立了完善的质量管理体系和检测机制。公司从原材料采购、生产过程到成品检验都实行严格的质量控制,确保每一件产品都符合客户的要求和行业标准。这种对质量的执着追求使得DDD公司的产品在市场上赢得了良好的口碑和信誉。

问答坊 | AI 解惑

次级同步整流及输出均流的集成控制器

次级同步整流及输出均流的集成控制器   摘要:CompactPCI(简称cPCI)电源在计算机、工业和电信领域的应用已经得到了认可。cPCI电源采用了标准的工业机械结构和高性能连接技术。然而,一般的cPCI电源沿用的是传统的二极管整流技术,应用时 ...…

查看全部问答>

ASP不能下载

买了一块FPGA的板子,是EP2C5T144的核心板,不知道为什么 不能用ASP下载,用JTGA能下,哪位高人指点一下。附上错误提示…

查看全部问答>

   WindowsCE开发历史变迁 

   WindowsCE开发历史变迁         十年了,我终于要离开这座城市了。收拾东西的时候居然翻出了一个多年前买的PDA。插上电源又看到了熟悉的WindowsCE开机画面,我感慨万千。原来这十年我身边始终没有改变过的只有WindowsCE而已。不管我 ...…

查看全部问答>

变频器原理介绍

变频器是利用电力半导体器件的通断作用将工频电源变换为另一频率的电能控制装置。我们现在使用的变频器主要采用交—直—交方式(VVVF变频或矢量控制变频),先把工频交流电源通过整流器转换成直流电源,然后再把直流电源转换成频率、电压均可控制的 ...…

查看全部问答>

嵌入式问题?

搞嵌入式最重要的是学习哪一部分啊,谢谢大家…

查看全部问答>

wince6.0在启动自己的应用程序会发出噗噗声及servicesStart.exe的作用

1、我在wince6.0上启动自己的应用程序,例如:\"Launch50\"=\"myapp.exe\"\"Depend50\"=hex:14,00,1e,00,怎么会发出噗噗的声音? 2、wince6.0下会启动这个.exe文件:\"Launch60\"=\"servicesStart.exe\",它的主要作用是什么?…

查看全部问答>

全方位的嵌入式学习开发资料

[转帖]全方位的嵌入式学习开发资料 因为资料较多,先整理这一部分,后续还会完善并相继推出ARM11 6410专区,以便大家学习交流。     如果这个帖子对您有帮助,烦请各位顶贴,小弟先谢谢了O(∩_∩)O (一)2440专区: 优秀论 ...…

查看全部问答>

关于移植uclinux启动部分的logo问题

版本是linux-2.4.x,根据参考资料修改了一下部分代码,make后移植进去打印信息中没有常见的ucliux的logo,直接进Sash command shell (version 1.1.1),然后就是命令行了。不知道在哪加这个logo的打印信息?…

查看全部问答>

变频器开关电源维修实例

以下是工程师在维修过程中,总结出来的一些经验,供大家参考,希望对大家能有所帮助。       开关电源的几个维修步骤如下:       1、检测整流电路D1—D4是否击穿或断路,滤波电路的电容是否损坏,平衡电 ...…

查看全部问答>

急~请教MSP430F5438A 的MCLK工作在最高频25MHz时如何设置内核工作电压?

请问F5438A 的工作在主频25MHz时该如何设置内核工作电压Vcore? 目前芯片工作在16MHz时正常,但当将MCLK设置在24M后不能进入主程序...看资料上说需要设置Vcore,但对着寄存器弄了半天也没搞定,求指教~~!!…

查看全部问答>