MM32搭建Web服务器——SSI和CGI接口

Foreword

今天周二加了个班,刚刚写完周一刘老师提的专利,还有半小时到9点半可以拿加班费,也不想干别的事了,就叨叨叨码点字。这两天二刷了《机智的医生生活》,里面的5个主角都是理想型,好希望能成为他们一样的人,有事业,有爱心,有个性,有友情。看到他们组的乐队,也很想组一个,不为拿格莱美,只为开心,为了以后回忆。今天跟ZL说了之后,兴奋了一下午,真是我志同道合的好朋友,距离我们SBG女子天团成团也有6年了,还没有作品演出,特别羡慕《北京东路的日子》,前几个月听了他们的十年荣耀版,希望我们的十年不会隐没于人潮之中,不会被生活压低了头,不会埋没了爱与希望。

又周五了,第一次没在公司里写完博客,我就是条咸鱼,我只想粘在床上……不能再一周加两天班了,心理生理都有点儿遭不住。春天有花,也有鼻窦炎,头疼的人脑子有点儿亢奋,但肉体只想当咸鱼。真的非常感谢学长的指导和帮助还有nizc愿意帮我们组分担点工作!

这周好气的事:那个第一个关注我CSDN的陌生人居然取关我!!!二话不说取关他!

Process Introduction

先介绍一下整个项目使用的软硬件环境:

硬件环境:MM32F3277、网线

软件环境:IAR、FreeRTOS、LWIP、makefsfile.exe

MM32作为服务器,在程序中固定一个IP地址,之后在浏览器中输出IP地址就可以访问相应的网页。浏览器和MM32服务器之间的请求应答过程如下:

  1. 在浏览器中输入IP地址(IP地址发出的是GET指令),浏览器向MM32发送GET指令数据,MM32通过函数httpd_init()http_accept()与TCP建立连接,函数http_recv()接收浏览器的请求数据

  2. MM32调用http_parse_request()函数提取接收到的字符串中是GET指令还是POST指令,接下来GET和POST调用的是不同的函数,这里先说GET指令

  3. MM32调用http_find_file()函数,判断指令内容是打开网页文件(htm,html,shtm,shtml文件),还是CGI程序指令(CGI是网页返回给服务器的表单)

  4. 如果要求打开网页文件,就在fs.c中打开网页转换好的二进制文件HTTP_FSDATA_FILE "fsdata.c"

  5. 通过http_init_file()函数对数据进行初始化,读取网页数据、长度等

  6. 调用函数http_send_data(),在httpd_cgi_ssi.c中查找网页中要求的SSI对应的Tag,添加对应的内容数据,将整体发送给浏览器

  7. 浏览器显示网页内容

关于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">&nbsp;&nbsp;号:<input type="text" placeholder="请输入账号名" name="username" required><br><br>&nbsp;&nbsp;码:<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

最后贴一段三毛的话:

西班牙有一句谚语:

“如果常常流泪,就不能看见星光”

我很喜欢这句话

所以即使要哭

也只在下午小哭一下

夜间要去看星

是没有时间哭的

再说,我还要去采果子呢

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值