lwip http server

在做web配网的时需要一个http server网上搜了下lwip有提供http server,下面记录下使用怎么使用和使用过程中遇到的问题

开始移植

网上找了些资料发现下面网址讲的比较好,介绍了cgi,ssi和移植方法,这里我再整理下
https://wenku.baidu.com/view/330c56204afe04a1b171de08.html

lwip版本:1.4.0

文件入下
lwip http server
下表分别介绍这些文件

文件(夹) 说明
makefsdata 该文件夹包含原始网页文件和将原始网页文件转化为网页数组的工具,主要目前时生成fsdata.c文件
fs.c 用来管理生成的网页数据
fs.h 用来管理生成的网页数据
fsdata.c 生成的网页数组
fsdata.h 生成的网页数组
httpd.c http server 的实现
httpd.h http server 的实现
httpd_cgi_ssi.c cgi和ssi实现,主要修改这里面代码

makefsdata文件夹如下,web_pages存放写好的网页,运行make.bat就生成fsdata.h
lwip http server
在wifi起ap后调用httpd.c 文件里的httpd_init,http server就初始化成功了,然后再浏览器输入wifi ip地址回车,就进入登陆上服务器了

有几个宏定义要打开

//filename :lwipopts.h
#define HTTPD_SUPPORT 1      //打开httpd
#define HTTPD_USE_MEM_POOL 0 //要关掉
#define LWIP_HTTPD_SUPPORT_POST   1 //支持post
#define LWIP_HTTPD_MAX_CGI_PARAMETERS 16    //cgi数量定义
#define LWIP_HTTPD_SSI_INCLUDE_TAG      1  //改为0就不发送标签
void httpd_init(void)
{
  LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\r\n"));

#if LWIP_HTTPD_SSI
  httpd_ssi_init(); //初始化ssi参数
#endif

#if LWIP_HTTPD_CGI
  httpd_cgi_init(); //初始化cgi参数
#endif

  httpd_init_addr(IP_ADDR_ANY); //建立一个tcp server 绑定IP_ADDR_ANY地址,端口号80
  
}

httpd_cgi_ssi.c中

void httpd_ssi_init(void)
{
	http_set_ssi_handler(SSIHandler,ppcTAGs,NUM_CONFIG_SSI_TAGS);
}

void httpd_cgi_init(void)
{
     http_set_cgi_handlers(ppcURLs, NUM_CONFIG_CGI_URIS);
}

ppcTAGs用于存放标签(等下结合网页看)

#define APLIST_TAG_NAME     	"aplist"
#define APLIST_COUNT_TAG_NAME 	"aplistcount"
#define APLIST_PAGE_TAG_NAME 	"aplistpage"

static const char *ppcTAGs[]=
{
	APLIST_TAG_NAME,       //index = 0
	APLIST_COUNT_TAG_NAME, //index = 1
	APLIST_PAGE_TAG_NAME,  //index = 2
};

注意:这个标签长度取决于下面宏定义
lwipopts.h

#define LWIP_HTTPD_MAX_TAG_NAME_LEN 20

SSIHandler是找到这些标签后对应的处理函数

static u16_t SSIHandler(int iIndex,char *pcInsert,int iInsertLen)
{
    char *TagName = NULL;
    //这里根据index找到对应Tag
    TagName = (char *)SSIGetTagName(iIndex);

    if (TagName == NULL) return 0;
	if (STRCMP(TagName, APLIST_TAG_NAME) == 0) //判断APLIST_TAG_NAME 标签
    {  //APLIST_TAG_NAME 标签下处理,输出pcInsert,这个变量是输出参数,放在web网页的对应标签后
        Aplist_Handler(pcInsert); 
    }
    else if (STRCMP(TagName, APLIST_COUNT_TAG_NAME) == 0)
    {
        AplistCount_Handler(pcInsert);
    }
    else if (STRCMP(TagName, APLIST_PAGE_TAG_NAME) == 0)
    {
        AplistPage_Handler(pcInsert);
    }

	return strlen(pcInsert); //返回pcInsert长度
}

注意:pcInsert这个这个指针被分配的内存取决下面宏
lwipopts.h

#define LWIP_HTTPD_MAX_TAG_INSERT_LEN 1024*2

ppcURLs用于存放请求的url地址,和不同请求不同网址下服务端执行的动作(等下结合网页看)

static const tCGI ppcURLs[]=
{   //   网址          服务端执行动作
    {"/login.cgi",LOGIN_CGI_Handler}, 
    {"/wifimode.cgi",WIFI_CGI_Handler},
    {"/scan.cgi",SCAN_CGI_Handler},
    {"/aplist.cgi",APLIST_CGI_Handler},
};

比如服务器收到请求的是http:xx.xx.xx.xx/login.cgi这个url后就会执行LOGIN_CGI_Handler这个函数
可以看到这是一个登陆函数,但是没有验证用户名和密码

//pcParam 是得到参数字符串 
//iNumParams 得到参数总个数
//iIndex 是对应参数序号
//pcValue[iIndex]得到对应参数字符串
const char* LOGIN_CGI_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
{
  LOG_PRINTF("LOGIN_CGI_Handler\r\n");

  iIndex = FindCGIParameter("username",pcParam,iNumParams);
  if (iIndex != -1)
  {
      LOG_PRINTF("username: %s\r\n", pcValue[iIndex]);
  }

  iIndex = FindCGIParameter("password",pcParam,iNumParams);
  if (iIndex != -1)
  {
      LOG_PRINTF("password: %s\r\n", pcValue[iIndex]);
  }
  return "/wireless_config.shtml"; //返回的网页
}

下面看下网页代码
index.shtml

  <form id="logonForm" name="logonForm" method="post" action="/login.cgi">

可以看到这里提交一个表单,方法是post,动作是/login.cgippcURLs第一个结构体对应上

/wireless_config.shtml

<p class="STYLE1" id="p_aplistpage" hidden="hidden"><!--#aplistpage--></p>
<p class="STYLE1" id="p_totalpage" hidden="hidden"><!--#aplistcount--></p>
<p class="STYLE1" id="p_aplist" hidden="hidden"><!--#aplist--></p>

上面这三个标签和ppcTAGs对应,当wifi通过ppcTAGs标签下对应的函数处理后加在对应标签后返回,网页端再拿着标签对应的id做处理显示出来。
发生请求后电脑网页端可以看到都加在对应标签后
lwip http server
大致就是这样的原理

下面说下遇到问题
1.只有httpd_init函数,没有提供deinit函数,我自己写个函数标记了下tcp的pcb,deinit就释放他

static struct tcp_pcb *httpd_close_pcb=NULL;
void httpd_close(void)
{
	LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_close\r\n"));
	if(httpd_close_pcb != NULL)
	{		
		printf("#### %d ###\n",tcp_close(httpd_close_pcb));	
		httpd_close_pcb= NULL;
	}
}


/**
 * Initialize the httpd with the specified local address.
 */
static void httpd_init_addr(struct ip_addr *local_addr)
{
  struct tcp_pcb *pcb;
  err_t err;

  pcb = tcp_new(); 
  httpd_close_pcb = pcb;  //add 这里是我加的
  LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
  tcp_setprio(pcb, HTTPD_TCP_PRIO);
  /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
  err = tcp_bind(pcb, local_addr, HTTPD_SERVER_PORT);
  LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
  pcb = tcp_listen(pcb); //-
  LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
  /* initialize callback arg and accept callback */
  tcp_arg(pcb, pcb);
  tcp_accept(pcb, http_accept);
}

2.发现每会发送请求后 tcp_in.c的 533 行都会分配内存,我一直想着怎么释放,偶然发现过一段时间后他自己释放了,百度后发现如下,看来要学下lwip
lwip http server
作者博客如下
https://blog.csdn.net/xujun3614/article/details/78193065

3.服务器执行动作的这个函数执行时间不能太长,这个是目前我还不知道怎么解决的,可以看到函数最后返回一个网页,若执行时间较长,浏览器就一直转圈圈,有可能加载失败。