Foreword
今天周二加了个班,刚刚写完周一刘老师提的专利,还有半小时到9点半可以拿加班费,也不想干别的事了,就叨叨叨码点字。这两天二刷了《机智的医生生活》,里面的5个主角都是理想型,好希望能成为他们一样的人,有事业,有爱心,有个性,有友情。看到他们组的乐队,也很想组一个,不为拿格莱美,只为开心,为了以后回忆。今天跟ZL说了之后,兴奋了一下午,真是我志同道合的好朋友,距离我们SBG女子天团成团也有6年了,还没有作品演出,特别羡慕《北京东路的日子》,前几个月听了他们的十年荣耀版,希望我们的十年不会隐没于人潮之中,不会被生活压低了头,不会埋没了爱与希望。
又周五了,第一次没在公司里写完博客,我就是条咸鱼,我只想粘在床上……不能再一周加两天班了,心理生理都有点儿遭不住。春天有花,也有鼻窦炎,头疼的人脑子有点儿亢奋,但肉体只想当咸鱼。真的非常感谢学长的指导和帮助还有nizc愿意帮我们组分担点工作!
这周好气的事:那个第一个关注我CSDN的陌生人居然取关我!!!二话不说取关他!
Process Introduction
先介绍一下整个项目使用的软硬件环境:
硬件环境:MM32F3277、网线
软件环境:IAR、FreeRTOS、LWIP、makefsfile.exe
MM32作为服务器,在程序中固定一个IP地址,之后在浏览器中输出IP地址就可以访问相应的网页。浏览器和MM32服务器之间的请求应答过程如下:
-
在浏览器中输入IP地址(IP地址发出的是GET指令),浏览器向MM32发送GET指令数据,MM32通过函数
httpd_init()
和http_accept()
与TCP建立连接,函数http_recv()
接收浏览器的请求数据 -
MM32调用
http_parse_request()
函数提取接收到的字符串中是GET指令还是POST指令,接下来GET和POST调用的是不同的函数,这里先说GET指令 -
MM32调用
http_find_file
()函数,判断指令内容是打开网页文件(htm
,html
,shtm
,shtml
文件),还是CGI程序指令(CGI是网页返回给服务器的表单) -
如果要求打开网页文件,就在
fs.c
中打开网页转换好的二进制文件HTTP_FSDATA_FILE "fsdata.c"
-
通过
http_init_file()
函数对数据进行初始化,读取网页数据、长度等 -
调用函数
http_send_data()
,在httpd_cgi_ssi.c
中查找网页中要求的SSI对应的Tag,添加对应的内容数据,将整体发送给浏览器 -
浏览器显示网页内容
关于GET指令的过程就是这样啦,POST指令的流程和方法下期再说。
SSI & CGI Introduction
说一下我自己对SSI和CGI的理解。SSI用在.shtm
文件中,以<!--#XXX-->
的形式写在网页文件中,在服务端接收到浏览器请求后,就会将网页文件中查找到的<!--#XXX-->
替换成服务器中的Tag对应内容,然后连同网页数据一起发送给浏览器。CGI是通过<form method=get action="/login.cgi">
的形式将表单内的数据发送给服务器。SSI用来从服务器那里获取数据,CGI就是给服务器发送数据。
Wikepedia上对SSI和CGI的解释:
服务器端包含(Server Side Includes)是一种简单的解释性服务器端脚本语言,几乎专门用于万维网。使用它的指令将一个或多个文件的内容包含到Web服务器上的Web页面中最有用
#include
。这通常可以是整个站点中的通用代码段,例如页面标题,页面页脚和导航菜单。SSI还包含用于条件功能的控制指令和用于调用外部程序的指令。它由Apache,LiteSpeed,nginx,IIS以及W3C的Jigsaw支持。它的根源是NCSA HTTPd。为了让Web服务器识别允许SSI的HTML文件,因此执行这些指令,无论是文件名应具有特殊结束扩展,默认情况下.shtml
,.stm
,.shtm
,或者,如果服务器被配置为允许这样做,设置执行文件的位。
通用网关接口(CGI)是一种接口规范,使Web服务器能够执行外部程序,通常可以处理用户请求。此类程序通常用脚本语言编写,通常称为CGI脚本,但它们可能包括编译后的程序。当Web用户在使用CGI的网页上提交Web表单时,就会发生典型的用例。表单的数据在HTTP请求中通过URL表示CGI脚本的形式发送到Web服务器。然后,Web服务器在新的计算机进程中启动CGI脚本,将表单数据传递给它。CGI脚本的输出,通常的形式HTML,是由脚本返回给Web服务器,服务器会将其返回到浏览器作为它的响应到浏览器的请求。
MM32——SSI
板端服务器对浏览器发送过来的请求的解析处理函数都是LWIP自带的,需要根据网页功能进行修改的只有httpd_cgi_ssi.c和httpd.c文件。
-
首先调用
ConfigWebInit()
函数,初始化SSI和CGI句柄void ConfigWebInit(void) { // Pass our tag information to the HTTP server. http_set_ssi_handler(ConfigSSIHandler, g_pcConfigSSITags, NUM_CONFIG_SSI_TAGS); // Pass our CGI handlers to the HTTP server. http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS); }
-
函数
http_set_ssi_handler(ConfigSSIHandler, g_pcConfigSSITags,NUM_CONFIG_SSI_TAGS)
中参数的解释:-
g_pcConfigSSITags
表示存放SSI标志的数组,例如:#define SSI_INDEX_MODNAME 0 #define SSI_INDEX_IPADDR 1 static const char *g_pcConfigSSITags[] = { "modname", // SSI_INDEX_MODNAME "ipaddr", // SSI_INDEX_IPADDR }
-
NUM_CONFIG_SSI_TAGS
表示SSI标志的个数:#define NUM_CONFIG_SSI_TAGS (sizeof(g_pcConfigSSITags) / sizeof (char *))
-
ConfigSSIHandler
表示SSI替换内容的句柄函数:static uint16_t ConfigSSIHandler(int iIndex, char *pcInsert, int iInsertLen){ // Which SSI tag are we being asked to provide content for? switch(iIndex) { // The mode name tag "modname" case SSI_INDEX_MODNAME: { char* modName; modName = "MM32"; return(snprintf(pcInsert, iInsertLen, "%s",modName)); } // The local IP address tag "ipaddr". case SSI_INDEX_IPADDR: { uint32_t ui32IPAddr; ui32IPAddr = lwipipaddr(); //printf("ui32IPAddr.length is %s",strlen((char)ui32IPAddr)); return(snprintf(pcInsert, iInsertLen, "%d.%d.%d.%d", ((ui32IPAddr >> 0) & 0xFF), ((ui32IPAddr >> 8) & 0xFF), ((ui32IPAddr >> 16) & 0xFF), ((ui32IPAddr >> 24) & 0xFF))); } default: { return(snprintf(pcInsert, iInsertLen, "<b><i>Tag %d unknown!</i></b>", iIndex)); } }
-
-
还有几个关于SSI比较重要的函数:
get_tag_insert()
:把shtm中的Tag找出来并添加指定内容,内容通过ConfigSSIHandler()判断http_send_data_ssi()
:把shtm中的<!--#XXX-->
替换成指定内容,发送过去
-
几个关于SSI比较重要的宏定义:
-
定义
<!--#XXX-->
中X的最大个数,在httpd_opts.h中#define LWIP_HTTPD_MAX_TAG_NAME_LEN 8
-
定义
<!--#XXX-->
替换内容的大小#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 256
-
-
Example
-
HTML
<label id="modname"> <!--#modname--> </label>
-
httpd_cgi_ssi.c
static uint16_t ConfigSSIHandler(int iIndex, char *pcInsert, int iInsertLen){ // Which SSI tag are we being asked to provide content for? switch(iIndex) { // The mode name tag "modname" case SSI_INDEX_MODNAME: { char* modName; modName = "MM32"; return(snprintf(pcInsert, iInsertLen, "%s",modName)); } default: { return(snprintf(pcInsert, iInsertLen, "<b><i>Tag %d unknown!</i></b>", iIndex)); } }
-
MM32——CGI
觉得CGI最经典的例子应该是登录,那就用这个例子来写把。注意表单里面必须用submit提交不能用button。
-
CGI初始化函数
http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS)
中参数的解释:-
g_psConfigCGIURIs
表示CGI请求和对应的handler函数,就是通过表单提交的不同的CGI请求来执行对应的handler函数。static const tCGI g_psConfigCGIURIs[] = { { "/login.cgi", ConfigLoginCGIHandler }, // CGI_INDEX_CHECKIN { "/webconfig.cgi", ConfigWebConfigCGIHandler }, // CGI_INDEX_WEBCONFIG };
-
NUM_CONFIG_CGI_URIS
表示CGI请求的个数#define NUM_CONFIG_CGI_URIS (sizeof(g_psConfigCGIURIs) / sizeof(tCGI))
-
-
Example(这里是用GET指令提交表单,下期讲POST)
-
HTML
<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-type" content="text/html;charset=UTF-8"> <title>用户登录 - MM32</title> </head> <body> <label> <h1>用户账号登录</h1> <hr><br><br> </label> <form action="login.cgi" method="GET" name="login"> 账 号:<input type="text" placeholder="请输入账号名" name="username" required><br><br> 密 码:<input type="password" placeholder="请输入密码" name="password" required><br><br><br> <input type="submit" name="checkinSubmit" value="login"> </form> </body> </html>
-
httpd_cgi_ssi.c
static const char * ConfigLoginCGIHandler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[]) { int verifyNum = 0; char *correctUsername = "admin"; char *correctPsd = "admin"; iIndex = FindCGIParameter("username",pcParam,iNumParams); if(iIndex != -1){ for(int i = 0; i < iNumParams; i ++){ if(strcmp(pcParam[i],"username") == 0){ if(strcmp(pcValue[i],correctUsername) == 0){ verifyNum++; } }else if(strcmp(pcParam[i],"password") == 0){ if(strcmp(pcValue[i],correctPsd) == 0){ verifyNum++; } } } } if(verifyNum == 2){ return(LOGIN_INDEX_URI); //#define LOGIN_INDEX_URI "/index.htm" }else{ return(LOGIN_ERROR_RESPONSE); //#define LOGIN_ERROR_RESPONSE "/404.htm" } }
-
-
最后,浏览器输入IP地址后打开的是哪一个网页是通过
httpd.c
中的httpd_default_filenames[]
数组决定的static const default_filename httpd_default_filenames[] = { {"/index.shtml", 0 }, {"/index.ssi", 1 }, {"/index.shtm", 1 }, {"/index.html", 0 }, {"/index.htm", 0 } };
下期预告
- GET和POST指令的区别
- POST指令如何使用
The End
最后贴一段三毛的话:
西班牙有一句谚语:
“如果常常流泪,就不能看见星光”
我很喜欢这句话
所以即使要哭
也只在下午小哭一下
夜间要去看星
是没有时间哭的
再说,我还要去采果子呢