精华内容
下载资源
问答
  • stm32 web服务器实现

    万次阅读 2016-12-27 19:44:07
    最近在做stm32 web服务器的东西,忙了一段时间终于弄完了,把这几天关于stm32服务器的工作记录一下。 刚接到这个任务的时候,不知道怎么下手,网上资料似乎不是很多,于是在下载了一个官方demo测试了一下,看了一下...

    最近在做stm32 web服务器的东西,忙了一段时间终于弄完了,把这几天关于stm32服务器的工作记录一下。

    刚接到这个任务的时候,不知道怎么下手,网上资料似乎不是很多,于是在下载了一个官方demo测试了一下,看了一下代码,不是很懂,于是继续百度找资料,找到一个比较有用的网页,以下是链接: 最近在做stm32 web服务器的东西,忙了一段时间终于弄完了,把这几天关于stm32服务器的工作记录一下。刚接到这个任务的时候,不知道怎么下手,网上资料似乎不是很多,于是在下载了一个官方demo测试了一下,看了一下代码,不是很懂,于是继续百度找资料,找到一个比较有用的网页,以下是链接:http://wenku.baidu.com/link?url=SfAxsft0bXxvaoeDtSGCLlnB3yQNhofQfLwfO9l-aZmTsVy2haookGZI6VP4WGlnr26Fx_BxakWV3oMQbKl54FQyssKDy2fRxI5JnIXaPgK

    其实,stm32 web服务器与pc网页的交互一般是以表单的方式,就两个接口,cgi和ssi。这两个东西我的理解是这样的,cgi 就是pc网页向stm32 web服务下发信息的接口,比如我们有这样的一个网页:

    <form method='get' action='config.cgi'>

    <p><label for='ip'>ip:</label><input type='text' name='ip' value="<!--#ip-->" maxlength="1" /></p>

    <p><label for='port'>port:</label><input type='text' name='port' value="<!--#port-->" maxlength="6" />

    </p><p><input type='submit' value='保存' /></p>

    </form>

    如果看不懂这个网页的话,就百度一下吧,把上面东西复制到Macromedia Dreamweaver 8软件上就能看到是什么页面,上面在form就是表单,在<form></form>里面的内容就是表单的内容,里面有两个输入框,和一个保存按钮,当我们点出保存的时候就会在web服务器里触发,config.cgi的接口,关于web服务器的内容,后面再说,然后会把这个表单里的内容下发到服务器里,比如会把上面ip和port输入框中的数据发到web服务器里,web服务器就能获得想要的数据。接下来说一下ssi,我们可以拿个网络抓包软件观察一下,pc网页的web服务器的通信过程,其实也就是pc发送一个请求,然后web服务器返回一组数据,这组数据就是整个网页的内容。ssi的工作就是在这组返回的数据中嵌入一个要发送加pc端的数据在里面,比如刚才那个表单网页数据,我们要显示一些数据到ip输入框中,怎么办呢?在没有ssi接口的时候可以看到,

    <p><labelfor='ip'>ip:</label><input type='text' name='ip' value="<!--#ip-->" maxlength="1" /></p>

    有ssi接口时候,我们就可以利用ssi接口改变返回的内容,比如:

    <p><labelfor='ip'>ip:</label><input type='text' name='ip' value="<!--#ip-->192.168.1.1" maxlength="1" /></p>

    可以看到 "<!--#ip-->192.168.1.1" 这个地方是不相同的,这时返回ip输入框是带着数据的,这个数据将显示在ip输入框内。 

    接下来分析一下代码,首先看一下stm32官方http web 包里的文件


    fsdata.c,fsdata.h,是网页转换成的数组, 

    fs.c, fs.h是操作fsdata.c里面数据的一些读写函数

    httpd.c, httpd.h函数是真正实现web服务器的文件

    httpd_cgi_ssi.c, httpd_cgi_ssi.h 就是刚才提到的cgi, ssi接口,

    一般我们只需要改动httpd_cgi_ssi.c, fsdata.c(这个文件是makefsfile.exe生成的),

    makefsfile里面又有一些什么文件呢


    fs文件夹是所有的网页,fsdata.c 是用makefsfile.exe转换得到的

    下面分析一下代码

    1. void httpd_init(void)

    {
      LWIP_DEBUGF(HTTPD_DEBUG, ("httpd_init\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);  //http web 相关初始化
    }

    2.ssi接口 要注意的一点就是 有ssi接口的网页要以 shtml为后缀

    char const* TAGS[]={  //这里定义了ssi标签,

        "ip",  //ip地址 对应网页里的<!--#ip-->
        "sp",  //服务器端口
    };

    u16_t DeviceSSI_Handler(int iIndex, char *pcInsert, int iInsertLen)
    {
        int len = 0;
        char buff[16];


        memset(buff,0,sizeof(buff));
        switch(iIndex)
        {
            case 0: //这个index是在数组中的位置
                sprintf(buff,"%s",“192.168.1.1”);
                len = strlen(buff);

                memcpy(pcInsert,buff,len);// 这里是拷贝填充在ip标签里的内容

                break;
            case 1:

        。。。。。。

        break;

        }

        return len;

    }

    3.cgi接口

    //cgi接口定义

    tCGI CGI_TAB[]={
        {"/login.cgi", LOGIN_CGI_Handler},
        {"/saveNet.cgi", SAVE_NET_Handler} /*这里的cginame要和网页表单里的action相同<form method='get' action='config.cgi'>*/
    };

    const char * SAVE_NET_Handler(int iIndex, int iNumParams, char *pcParam[], char *pcValue[])
    {
        uint32_t i=0;

        for (i=0; i<iNumParams/*参数数量*/; i++)
        {
            if(strcmp(pcParam[i]/*参数名字,与网页里控件的name相同name='ip'*/,"ip") == 0) //设备ip
            {

        printf("ip is : %s \r\n",pcValue[i]/*控件的值*/);
            }
            else if(strcmp(pcParam[i],"port") == 0)  
            {

        ...

    }

         ......

    }

    cgi和ssi大概就这些内容要改的,

    4.打开网页里是哪一个为首页呢?在httpd.c里有一个数据

    //这里设置首页显示的html ,所以首页就是login
    const default_filename g_psDefaultFilenames[] = {
      {"/login.shtml", true },
      {"/login.ssi", true },
      {"/login.shtm", true },
      {"/login.html", false },
      {"/login.htm", false }
    };

    5.我们发送ssi标签时会把标签一起发上去,这样在编辑控件里是会把标签一起显示的,比如value="<!--ip-->192.168.1.1",这不是我们想要的,怎么让它不发送标签呢,

    把httpd.c里的定义  #define LWIP_HTTPD_SSI_INCLUDE_TAG           1  //改为0就不发送标签

    6.还有一些比较常用的宏定义

    #define LWIP_HTTPD_MAX_CGI_PARAMETERS 16    //cgi数量定义

    #define LWIP_HTTPD_MAX_TAG_NAME_LEN 8       //定义tag标签长度定义

    #define LWIP_HTTPD_MAX_TAG_INSERT_LEN 192   //定义返回tag内容长度


    展开全文
  • stm32搭建web服务器

    2016-12-06 16:43:06
    stm32搭建web服务器,修改于官方移植lwip搭建的web服务器 硬件基础:stm32f407 + lan9303
  • STM32实现Web服务器

    2017-12-26 10:35:21
    有例程及详细的讲解,适用于初学嵌入式WebServer的同学下载。
  • STM32 lwip建立web服务器,基于STM32的串口服务器web功能实现
  • 基于STM32的嵌入式web服务器的设计 个人感觉还可以,还不错的。。。。。。。
  • 此工程MCU采用STM32F407芯片,网络芯片采用的是LAN8720A,主要功能为:通过页面浏览器客户端完成对STM32相关的运行参数进行读取和修改。网络部分加入AJAX技术,使读取设备参数时页面浏览器非整页刷新,而是部分...
  • stm32上开发webserver的例程
  • STM32F407 WEB服务器 开发
  • 基于Stm32的嵌入式WebServer例程,注释齐全,简单易懂,适合初学者
  • 此工程MCU采用STM32F407芯片,网络芯片采用的是LAN8720A,主要功能为:通过页面浏览器客户端完成对STM32相关的运行参数进行读取和修改。网络部分加入AJAX技术,使读取设备参数时页面浏览器非整页刷新,而是部分...
  • 基于STM32 HAL和FreeRTOS的HTTP Web服务器 基于FreeRTOS的简单HTTP服务器以及适用于Nucleo-F746ZG板的STM32 HAL库。 使用LwIP Netconn API。 它包含两个HTML页面: 第一页(主页)是静态的,它提供了有关STM32F7和...
  • STM32作TCP服务器实现PC多客户端连接的模板。本模板是基于正点原子的STM32F750开发板上实现的,多连接处理部分的代码在tcp_server_demo.c中,其他型号型号可参考修改。
  • stm32实现web服务器

    热门讨论 2012-08-03 10:36:33
    通过移植lwip在STM32实现一个web服务器
  • 基于STM32的串口服务器web功能实现.pdf
  • 简单的web实现,在我以前的TCP修改的
  • 本DEMO为工作项目的部分内容,STM32f107 作为TCP客户端访问WEB站点 ,使用http/1.1格式上传并获取WEB数据。附件WORD文档为项目的接口格式说明。 应用层:HTTP数据格式解析 MAX层:stm32f107 传输层:LWIP协议栈
  • STM32 WEBSERVER源码

    2017-07-09 12:26:17
    STM32 WEBSERVER源码,正点原子探索者开发板
  • STM32 WebServer

    2021-03-11 11:20:50
    3.11 链接: 用单片机实现HTTP网页服务器功能(详细教程). 只完成了这个博客的进度。 立个Flag,鼓励自己完成个漂亮的web

    3.11

    链接: 用单片机实现HTTP网页服务器功能(详细教程).
    只完成了这个博客的进度。
    立个Flag,鼓励自己完成个漂亮的web。

    展开全文
  • 使用STM32的LWIP中间件搭建Web服务器

    千次阅读 2020-04-02 21:16:15
    前言 HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的种网络传输协议,所有的WWW文件都必须... ...浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器有...

    前言

    HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的种网络传输协议,所有的WWW文件都必须遵守这个标准。HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。

    HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。Web服务器有:Apache服务器,IIS服务器(Internet Information Services)等。Web服务器根据接收到的请求后,向客户端发送响应信息。HTTP默认端口号为80,但是你也可以改为8080或者其他端口。

    本节我们将学习stm32 + LWIP实现一个HTTP服务器的搭建。具体实现请看下文。

    示例详解

    基于硬件平台: MCU 的型号是 STM32F407VGT6, 使用stm32cubemx 工具自动产生的配置工程,使用KEIL5编译代码。本示例所用的最小系统板原理图:

     

     

    ————————————————
    版权声明:本文为CSDN博主「张礼富」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhanglifu3601881/article/details/90300187

    展开全文
  • STM32+ENC28J60+无线路由实现物联网模拟控制系统基本思路是:把WEB服务器写入stm32内,通过以太网接口连接stm32和路由。在路由中设置WEB服务器能被访问并且绑定一个域名。stm32可以扩展控制很多东西,例如器件...
  • STM32移植lwip之建立web服务器

    万次阅读 多人点赞 2016-12-03 09:32:54
    本篇目标:在之前能ping通pc机的工程基础上搭建web服务器,...搭建工程:最终搭建好的web服务器工程(STM32搭建web服务器工程) 调试工具:用来调试tcp连接下的数据接收(网络调试助手) 测试浏览器:这里使用的是Ch

    本篇目标:在之前能ping通pc机的工程基础上搭建web服务器,借鉴官方web服务器的程序与网页,能够用pc机浏览器访问web服务器,并返回设置的网页

    材料准备:

    ps:通过修改官方搭建web服务器的代码,来了解搭建的过程,其中暂时去掉了ssi和cgi的程序,仅仅实现网页数据的返回和网页的跳转,并将官方的代码简化到相对最简,以便以后的学习之用


    浏览器请求指令探索

    要搭建服务器,首先肯定要先了解远程客户端是怎么访问服务器的,这里pc机的浏览器则作为客户端:

    1. 打开浏览器(谷歌浏览器测试),输入服务器ip;
    2. 浏览器发送请求命令给服务器;
    3. 服务器接收到指令后,通过程序来解析指令,找到对应应该返回的网页;
    4. 服务器发送网页代码给浏览器;
    5. 浏览器显示网页;

    接下来再用搭建虚拟服务器的方法,来模拟一下上面的过程:

    • 打开网络调试助手,切换到网络服务器,在服务器操作端口输入80,点击创建,如图;这样我们就创建了一个虚拟的服务器,这个服务器的ip就是pc机的本地ip
      搭建虚拟服务器

    • 查看确认一下本地ip地址,可以在网络连接里面查看,也可以在cmd输命令查看,这里的ip地址为192.168.6.104,如图:
      本地ip地址

    • 打开浏览器(谷歌浏览器测试),输入刚才确认的本地ip地址,这里输入192.168.6.104:
      浏览器访问

    • 返回去看看刚才搭建的服务器有什么变化,会发现有接收到的数据,只要重点观察第一行的数据“GET / HTTP/1.1”,这个字符串将会被服务器解析,然后将网页代码返回回去:
      客户端请求连接

    • 找到一个官方程序有一个fs文件夹,里面有已经做好的网页,打开网页index.html,右击-查看源代码,然后全选复制下来,在网络调试助手的发送区粘贴,并点击发送,如图:
      服务器返回数据

    • 这时,会发现浏览器已经显示了一张网页,但是好像又有点不全,因为图片没有显示,为什么呢?返回网络调试助手,发现接收区又有好多请求,看字面意思,好像就是图片的请求,然而服务器没有返回图片数据,所以图片无法显示

    • 这时候,将所有的浏览器请求列出来比较一下:
      “GET / HTTP/1.1”
      “GET /STM32F4x7_files/ST.gif HTTP/1.1”
      “GET /inchtml-pages-stm32_connectivity_files/pixel.gif HTTP/1.1”
      “GET /STM32F4x7_files/stm32.jpg HTTP/1.1”
      发现请求中 / 后面一部分的内容不相同,所以服务器只需要解析这一部分的字符串内容,来返回对应的网页数据即可


    搭建web服务器

    现在创建一个新的c文件,取名为 http_server.c ,接下来写几个函数来建立web服务器,抽重要的函数进行总结一下:

    • web服务器初始化函数 Http_Server_Init():
    void Http_Server_Init(void)
    {
    	struct tcp_pcb *http_server_pcb;
    
    	/* 为web服务器分配一个tcp_pcb结构体 */
    	http_server_pcb = tcp_new();
    		
    	/* 绑定本地端号和IP地址 */
    	tcp_bind(http_server_pcb, IP_ADDR_ANY, 80);
    
    	/* 监听之前创建的结构体http_server_pcb */
    	http_server_pcb = tcp_listen(http_server_pcb);
    	
    	/* 初始化结构体接收回调函数 */
    	tcp_accept(http_server_pcb, http_server_accept);
    }
    

    小结:上面函数主要就是为搭建web服务器做准备,包括申请网络结构体、设置80端口号、监听数据、设置接收数据回调函数;

    • 接收数据回调函数 tcp_server_accept() :
    static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
    {
    	struct http_state *hs;
    	
    	/* 分配内存空间 */
    	hs = (struct http_state *)mem_malloc(sizeof(struct http_state));
    	
    	if (hs != NULL)
    	{
    		memset(hs, 0, sizeof(struct http_state));
    	}
    		
    	/* 确认监听和连接 */
    	tcp_arg(pcb, hs);
    		
    	/* 配置接收回调函数 */
    	tcp_recv(pcb, http_server_recv);
    
    	/* 配置轮询回调函数 */
    	tcp_poll(pcb, http_server_poll, 4);
    	
    	/* 配置发送回调函数 */
    	tcp_sent(pcb, http_sent);
    	
    	return ERR_OK;
    }
    

    小结:函数中主要配置一些回调函数,比如接收,轮询,发送;

    • 接收数据处理函数 http_server_recv() :
    static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *http_recv_pbuf, err_t err)
    {
    	err_t parsed = ERR_ABRT;
    	struct http_state *hs = (struct http_state *)arg;
    
    	/* 告诉tcp已经接收到数据 */
    	tcp_recved(pcb, http_recv_pbuf->tot_len);
    	
    	if (hs->handle == NULL)
    	{
    		 /* 解析接收到的浏览器请求数据 */
    		 parsed = http_parse_request(&http_recv_pbuf, hs, pcb);
    	}
    		
        /* 清空请求字符串 */
    	if (parsed != ERR_INPROGRESS) 
    	{		
            if (hs->req != NULL) 
    	    {
                pbuf_free(hs->req);
                hs->req = NULL;
            }
        }
    		
    	if (parsed == ERR_OK)
    	{
    		/* 发送网页数据 */
    		http_send_data(pcb, hs);
    	}
    	else if (parsed == ERR_ARG)
    	{
    		/* 关闭连接 */
    		close_conn(pcb, hs);
    	}
    
    	return ERR_OK;
    }
    

    小结:函数主要工作将接收到的数据放入 http_parse_request() 函数进行解析,然后把网页数据发送出去;

    • 接收数据解析函数 http_parse_request():
    static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb)
    {
    	char *data;
    	char *crlf;
    	u16_t data_len;
    	struct pbuf *p = *inp;
    	
    	char *sp1, *sp2;
    	u16_t uri_len;
    	char *uri;
    	
    	/* 排列字符串 */
    	if (hs->req == NULL)
    	{
    		hs->req = p;
    	}
    	else
    	{
            /* 将多次的请求字符串进行连接排序 */
    		pbuf_cat(hs->req, p);
    	}
    		
    	/* 拷贝输入数据 */ 
        if (hs->req->next != NULL)
        {
    		data_len = hs->req->tot_len;
            pbuf_copy_partial(hs->req, data, data_len, 0);
        }
    	else
    	{
    		data = (char *)p->payload;
    		data_len = p->len;
    	}
    		
    	/* 提取接收到的浏览器字符串,浏览器请求示例:"GET / HTTP/1.1" */
    	if (data_len > 7) 
    	{
    		crlf = strstr(data, "\r\n");
    		
    		if (crlf != NULL) 
    		{
    			/* 比较前4个字符是否为 "GET " */
    			if (strncmp(data, "GET ", 4) == 0) 
    			{
    				/* sp1指向字符串 "/ HTTP/1.1" */
    				sp1 = (data + 4);
    			}
    			
    			/* 在sp1字符串中寻找字符" ",sp2指向字符串 " HTTP/1.1" */
    			sp2 = strstr(sp1, " ");
    			
    			/* uri_len获取sp1字符串首地址到sp2字符串首地址的长度 */
    			uri_len = sp2 - (sp1);
    
    			if ((sp2 != 0) && (sp2 >= (sp1))) 
    			{
    				/* 将解析的字符串赋给uri,并在最后加上结束符\0,
    				   uri指向字符串 "/\0" */
    				uri = sp1;
    				*(sp1 - 1) = 0;
    				uri[uri_len] = 0;
    				
    				/* 根据字符串寻找对应网页数据 */
    				return http_find_file(hs, uri, 0); 
    			}
    		}
    	}
    		
    	return ERR_OK;
    }
    

    小结:这个函数是重要的请求数据解析函数,函数将接收到的字符串(例:“GET /STM32F4x7_files/ST.gif HTTP/1.1”)
    分离出重要的判断字符串(例:“ /STM32F4x7_files/ST.gif”),然后根据这个字符串的内容来读取对应的网页数据;

    • 读取对应网页数据函数 http_find_file():
    static err_t http_find_file(struct http_state *hs, const char *uri, int is_09)
    {
    	struct fs_file *file = NULL;
    
    	/* 如果字符串为 "/\0",则打开index网页 */
    	if((uri[0] == '/') && (uri[1] == 0)) 
    	{
    		file = fs_open("/index.html");
    	    uri = "/index.html";
    	} 
    	else 
    	{
    		/* 如果为其他请求,则打开相应网页 */
    	    file = fs_open(uri);
    	}
    	
    	/* 将网页文件数据赋值给http_state结构体,之后发送出去 */
    	return http_init_file(hs, file, is_09, uri);
    }
    

    小结:此函数就是网页数据读取函数,里面最重要的函数就是 fs_open() 函数了,这个函数在官方建立web服务器工程里的 fs.c 文件里面,这个函数的解析放到后面;

    ps:http_server.c 还有头文件的包含,函数的定义;另外再编写一个http_server.h文件,包含宏定义,结构体定义,函数定义;在下面贴出这两个文件的源码;

    上面基本包括了几个重要的函数,当然还有其他的函数,包括发送函数等等,这些函数可以看源代码的注释来理解


    文件源码##

    • http_server.c
    #include "lwip/debug.h"
    #include "lwip/stats.h"
    #include "lwip/tcp.h"
    #include "http_server.h"
    #include "fs.h"
    
    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    /*
    *********************************************************************************************************
    *                                            LOCAL TABLES
    *********************************************************************************************************
    */
    static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err);
    static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err);
    static err_t http_server_poll(void *arg, struct tcp_pcb *pcb);
    static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri);
    static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
    static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb);
    static u8_t http_send_data(struct tcp_pcb *pcb, struct http_state *hs);
    static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len);
    static void close_conn(struct tcp_pcb *pcb, struct http_state *hs);
    
    /*
    *********************************************************************************************************
    *                                      LOCAL FUNCTION PROTOTYPES
    *********************************************************************************************************
    */
    
    /***
     * 函数名称 : Http_Server_Init();
     *
     * 函数描述 : web服务器初始化;
     *
     * 传递值	  : 无;
     *
     * 返回值   : 无;
     *
     **/
    void Http_Server_Init(void)
    {
    	struct tcp_pcb *http_server_pcb;
    
    	/* 为web服务器分配一个tcp_pcb结构体 */
    	http_server_pcb = tcp_new();
    		
    	/* 绑定本地端号和IP地址 */
    	tcp_bind(http_server_pcb, IP_ADDR_ANY, 80);
    
    	/* 监听之前创建的结构体http_server_pcb */
    	http_server_pcb = tcp_listen(http_server_pcb);
    	
    	/* 初始化结构体接收回调函数 */
    	tcp_accept(http_server_pcb, http_server_accept);
    }
    
    /***
     * 函数名称 : http_server_accept();
     *
     * 函数描述 : lwip数据接收回调函数,包含对tcp连接的确认,接收回调函数的配置;
     *
     * 传递值	  : *arg, *pcb, err ;
     *
     * 返回值   : ERR_OK 无错误;
     *
     **/
    static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
    {
    	struct http_state *hs;
    	
    	/* 分配内存空间 */
    	hs = (struct http_state *)mem_malloc(sizeof(struct http_state));
    	
    	if (hs != NULL)
    	{
    		memset(hs, 0, sizeof(struct http_state));
    	}
    		
    	/* 确认监听和连接 */
    	tcp_arg(pcb, hs);
    		
    	/* 配置接收回调函数 */
    	tcp_recv(pcb, http_server_recv);
    
    	/* 配置轮询回调函数 */
    	tcp_poll(pcb, http_server_poll, 4);
    	
    	/* 配置发送回调函数 */
    	tcp_sent(pcb, http_sent);
    	
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : http_server_recv();
     *
     * 函数描述 : 接受到数据后,根据接收到数据的内容,返回网页;
     *
     * 传递值	  : *arg, *pcb, *http_recv_pbuf, err;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *http_recv_pbuf, err_t err)
    {
    	err_t parsed = ERR_ABRT;
    	struct http_state *hs = (struct http_state *)arg;
    
    	/* 告诉tcp已经接收到数据 */
    	tcp_recved(pcb, http_recv_pbuf->tot_len);
    	
    	if (hs->handle == NULL)
    	{
    		/* 解析接收到的浏览器请求数据 */
    		parsed = http_parse_request(&http_recv_pbuf, hs, pcb);
    	}
    		
        /* 清空请求字符串 */
    	if (parsed != ERR_INPROGRESS) 
    	{		
            if (hs->req != NULL) 
    		{
                pbuf_free(hs->req);
                hs->req = NULL;
            }
        }
    		
    	if (parsed == ERR_OK)
    	{
    		/* 发送网页数据 */
    		http_send_data(pcb, hs);
    	}
    	else if (parsed == ERR_ARG)
    	{
    		/* 关闭连接 */
    		close_conn(pcb, hs);
    	}
    
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : http_server_poll();
     *
     * 函数描述 : 轮询函数;
     *
     * 传递值	  : *arg, *pcb;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_server_poll(void *arg, struct tcp_pcb *pcb)
    {
    	struct http_state *hs = arg;
    	
    	if (hs == NULL)
    	{
    		close_conn(pcb, hs);
    		return ERR_OK;
    	}
    	else
    	{
    		hs->retries++;
    		if (hs->retries == 4)
    		{
    			close_conn(pcb, hs);
    			return ERR_OK;
    		}
    				
            /* 如果连接存在打开的文件,则将会发送剩下的数据;
             * 如果一直没有收到GET请求,那么连接将会立刻关闭 */
    		if (hs && (hs->handle))
    		{
    			if (http_send_data(pcb, hs))
    			{
    				tcp_output(pcb);
    			}
    		}
    	}
    		
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : http_parse_request();
     *
     * 函数描述 : 对接收到的数据进行解析,根据不同的浏览器请求,返回对应的网页数据;
     *
     * 传递值	  : **inp, *hs, *pcb;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb)
    {
    	char *data;
    	char *crlf;
    	u16_t data_len;
    	struct pbuf *p = *inp;
    	
    	char *sp1, *sp2;
    	u16_t uri_len;
    	char *uri;
    	
    	/* 排列字符串 */
    	if (hs->req == NULL)
    	{
    		hs->req = p;
    	}
    	else
    	{
    	    /* 将多次的请求字符串进行连接排序 */
    		pbuf_cat(hs->req, p);
    	}
    		
    	/* 拷贝输入数据 */ 
        if (hs->req->next != NULL)
        {
    		data_len = hs->req->tot_len;
            pbuf_copy_partial(hs->req, data, data_len, 0);
        }
    	else
    	{
    		data = (char *)p->payload;
    		data_len = p->len;
    	}
    		
    	/* 提取接收到的浏览器字符串,浏览器请求示例:"GET / HTTP/1.1" */
    	if (data_len > 7) 
    	{
    		crlf = strstr(data, "\r\n");
    		if (crlf != NULL) 
    		{
    			/* 比较前4个字符是否为 "GET " */
    			if (strncmp(data, "GET ", 4) == 0) 
    			{
    				/* sp1指向字符串 "/ HTTP/1.1" */
    				sp1 = (data + 4);
    			}
    			/* 在sp1字符串中寻找字符" ",sp2指向字符串 " HTTP/1.1" */
    			sp2 = strstr(sp1, " ");
    			/* uri_len获取sp1字符串首地址到sp2字符串首地址的长度 */
    			uri_len = sp2 - (sp1);
    
    			if ((sp2 != 0) && (sp2 >= (sp1))) 
    			{
    				/* 将解析的字符串赋给uri,并在最后加上结束符\0,
    				   uri指向字符串 "/\0" */
    				uri = sp1;
    				*(sp1 - 1) = 0;
    				uri[uri_len] = 0;
    							
    				/* 根据字符串寻找对应网页数据 */
    				return http_find_file(hs, uri, 0); 
    				}
    			}
    	}
    		
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : http_find_file();
     *
     * 函数描述 : 对提取的数据进行判断,读取对应的网页数据;
     *
     * 传递值	  : *hs, *uri, is_09;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_find_file(struct http_state *hs, const char *uri, int is_09)
    {
    	struct fs_file *file = NULL;
    
    	/* 如果字符串为 "/\0",则打开index网页 */
    	if((uri[0] == '/') && (uri[1] == 0)) 
    	{
    		file = fs_open("/index.html");
    	    uri = "/index.html";
    	} 
    	else 
    	{
    		/* 如果为其他请求,则打开相应网页 */
    	    file = fs_open(uri);
    	}
    	
    	/* 将网页文件数据赋值给http_state结构体,之后发送出去 */
    	return http_init_file(hs, file, is_09, uri);
    }
    
    /***
     * 函数名称 : http_init_file();
     *
     * 函数描述 : 将要发送的数据保存到http_state结构体当中;
     *
     * 传递值	  : *hs, *file, is_09, *uri;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri)
    {
    	if (file != NULL) 
    	{
    	    hs->handle = file;
    		/* 将网页数据赋值给http_state */
    	    hs->file = (char*)file->data;
    		/* 将网页长度赋值给http_state */
    	    hs->left = file->len;
    	    hs->retries = 0;
    	} 
    	else 
    	{
    	    hs->handle = NULL;
    	    hs->file = NULL;
    	    hs->left = 0;
    	    hs->retries = 0;
    	}
    
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : http_send_data();
     *
     * 函数描述 : 数据发送函数;
     *
     * 传递值	  : *pcb, *hs;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static u8_t http_send_data(struct tcp_pcb *pcb, struct http_state *hs)
    {
    	err_t err = ERR_OK;
    	u16_t len;
    	u8_t data_to_send = 0;
    		
    	/* 配置发送数据长度,如果发送数据过长则分批发送 */
    	if (tcp_sndbuf(pcb) < hs->left)
    	{
    		len = tcp_sndbuf(pcb);
    	}
    	else
    	{
    		len = (u16_t)hs->left;
    	}		
    
        /* 发送网页数据 */
    	err = tcp_write(pcb, hs->file, len, 1);
    		
    	if (err == ERR_OK)
    	{
    		data_to_send = 1;
    		hs->file += len;
    		hs->left -= len;
    	}
    		
    	if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0))
    	{
            /* 关闭连接 */
    		close_conn(pcb, hs);
    		return 0;
    	}
    		
    	return data_to_send;
    }
    
    
    /***
     * 函数名称 : http_sent();
     *
     * 函数描述 : 数据已经被发送,并且被远程主机确定;
     *
     * 传递值	  : *arg, *pcb, len;
     *
     * 返回值   : ERR_OK无错误;
     *
     **/
    static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
    {
    	struct http_state *hs = (struct http_state *)arg;
    	
    	if (hs == NULL)
    	{
    		return ERR_OK;
    	}
    	
    	hs->retries = 0;
    
    	http_send_data(pcb, hs);
    
    	return ERR_OK;
    }
    
    /***
     * 函数名称 : close_conn();
     *  * 函数描述 : 关闭tcp连接;
     *  * 传递值	  : *pcb, *hs;
     *  * 返回值   : 无;
     *  **/
    static void close_conn(struct tcp_pcb *pcb, struct http_state *hs)
    {
    	tcp_arg(pcb, NULL);
    	tcp_recv(pcb, NULL);
    	tcp_err(pcb, NULL);
    	tcp_poll(pcb, NULL, 0);
    	tcp_sent(pcb, NULL);
    	
        if (hs != NULL) 
    	{
    		if(hs->handle) 
    		{
    			fs_close(hs->handle);
    			hs->handle = NULL;
    		}
    			mem_free(hs);
    	}
    
    	tcp_close(pcb);
    }
    
    • http_server.h
    #ifndef HTTP_SERVER_H
    #define HTTP_SERVER_H
    
    /*
    *********************************************************************************************************
    *                                              INCLUDE FILES
    *********************************************************************************************************
    */
    
    
    /*
    *********************************************************************************************************
    *                                               CONSTANTS
    *********************************************************************************************************
    */
    
    
    /*
    *********************************************************************************************************
    *                                             PERIPH DEFINES
    *********************************************************************************************************
    */
    
    
    /*
    *********************************************************************************************************
    *                                               DATA TYPES
    *********************************************************************************************************
    */
    
    
    /*
    *********************************************************************************************************
    *                                            GLOBAL VARIABLES
    *********************************************************************************************************
    */
    
    struct http_state {
      struct fs_file *handle;
      char *file;       /* Pointer to first unsent byte in buf. */
    
    #if 1
      struct pbuf *req;
    #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */
    
    #if 1
      char *buf;        /* File read buffer. */
      int buf_len;      /* Size of file read buffer, buf. */
    #endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
      u32_t left;       /* Number of unsent bytes in buf. */
      u8_t retries;
    #if 0
      const char *parsed;     /* Pointer to the first unparsed byte in buf. */
    #if 1
      const char *tag_started;/* Poitner to the first opening '<' of the tag. */
    #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
      const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
      u32_t parse_left; /* Number of unparsed bytes in buf. */
      u16_t tag_index;   /* Counter used by tag parsing state machine */
      u16_t tag_insert_len; /* Length of insert in string tag_insert */
    #if 0
      u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
    #endif /* LWIP_HTTPD_SSI_MULTIPART */
      u8_t tag_check;   /* true if we are processing a .shtml file else false */
      u8_t tag_name_len; /* Length of the tag name in string tag_name */
      char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
      char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
      enum tag_check_state tag_state; /* State of the tag processor */
    #endif /* LWIP_HTTPD_SSI */
    #if 0
      char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
      char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
    #endif /* LWIP_HTTPD_CGI */
    #if 0
      const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
      u16_t hdr_pos;     /* The position of the first unsent header byte in the
                            current string */
      u16_t hdr_index;   /* The index of the hdr string currently being sent. */
    #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
    #if 0
      u32_t time_started;
    #endif /* LWIP_HTTPD_TIMING */
    #if 0
      u32_t post_content_len_left;
    #if 0
      u32_t unrecved_bytes;
      struct tcp_pcb *pcb;
      u8_t no_auto_wnd;
    #endif /* LWIP_HTTPD_POST_MANUAL_WND */
    #endif /* LWIP_HTTPD_SUPPORT_POST*/
    };
    
    /*
    *********************************************************************************************************
    *                                                 MACRO'S
    *********************************************************************************************************
    */
    
    
    
    /*
    *********************************************************************************************************
    *                                           FUNCTION PROTOTYPES
    *********************************************************************************************************
    */
    
    void Http_Server_Init(void);
    
    /*
    ********************************************************************************************************
    *                                             MODULE END
    *********************************************************************************************************
    */
    
    #endif /* HTTP_SERVER_H */
    

    官方部分函数解析

    读取网页数据文件 fs.c (路径:Project->Standalone->web_server->http):

    struct fs_file *fs_open(const char *name)
    {
    	struct fs_file *file;
    	const struct fsdata_file *f;
    
    	/* 分配空间 */
    	file = fs_malloc();
    	if(file == NULL) 
    	{
    		return NULL;
    	}
    
    	for(f = FS_ROOT; f != NULL; f = f->next) 
    	{
    		/* 循环比较,如果输入的请求与网页的头数据一致,则返回该网页数据 */
    	    if (!strcmp(name, (char *)f->name)) 
    	    {
    		    file->data = (const char *)f->data;
    		    file->len = f->len;
    		    file->index = f->len;
    		    file->pextension = NULL;
    		    file->http_header_included = f->http_header_included;
    
    		    return file;
    		}
    	}
    	fs_free(file);
    	return NULL;
    }
    

    这里关注 f 变量的结构体 fsdata_file,定义在 fsdata.h:

    struct fsdata_file 
    {
    	const struct fsdata_file *next;
    	const unsigned char *name;
    	const unsigned char *data;
    	int len;
    	u8_t http_header_included;
    };
    

    **结构体中有三个重要的变量*next、name、data
    而在文件fsdata.c中,拉到最后,发现有几个 fsdata_file 的结构体变量,取其中一个来解析一下:

    const struct fsdata_file file__index_html[] = 
    { 
    	{
    		/* 变量*next,指向下一个要循环比较的数据 */
    		file__404_html,
    		/* 变量*name,指向数据数组 data__index_html[] */
    		data__index_html,
    		/* 变量*data,指向数据数组 data__index_html[]12个之后的数据 */
    		data__index_html + 12,
    		/* 网页数据长度 */
    		sizeof(data__index_html) - 12,
    		1,
    	}
    };
    
    • 变量*name指向的数组前12个数据是字符串 “/index.html” 的ascii码,用于与输入的浏览器请求 “GET /index.html HTTP/1.1” 进行对比;
    • 而*data指向数组的12个后的数据,便是网页源代码的16进制数据,这些数据将会由发送函数发送给浏览器,使浏览器显示网页;

    web服务器测试

    将工程编译后,烧进stm32,将网线与pc机连接:

    • 打开浏览器(谷歌浏览器测试)
    • 输入服务器ip(这里搭建的服务器ip:192.168.0.10),Enter;
    • 浏览器会显示网页,点击网页上的按钮即可以切换不同的网页

    如图:
    服务器返回网页数据


    总结:从上面的一系列过程可以get到搭建web服务器的核心思想,然而,现在并没有加入ssi和cgi,所以还无法用网页控制stm32,后面会加上ssi、cgi、post与get请求来完善整个web服务器;

    ps:有部分细节的地方解析的不是很清楚,而且自己也没有想明白,需要再加把劲看一些其他的资料来填补空白,共勉~

    展开全文
  • stm32F207利用DP8384实现100MHz网口通信,利用开发板实现
  • 初始IP地址是192.168.1.111,通过对PB7的操作来实现缺省值和flash之间的切换,设置好的参数写入flash,w5500接的是SPI2,单片机是stm32f103c8t6。 第一个网页没有任何修改权限,只有输入登录密码(初始密码:123456 ...
  • stm32f107 web server

    2017-04-18 22:19:25
    基于stm32f107 web服务器开发源码;其实现方式采用html+cgi+ssi技术,通过makefsdata将html文件转换成.c文件;
  • 基于STM32F429使用RT-Thread实现web服务器功能

    千次阅读 多人点赞 2020-10-26 22:52:44
    目录 参考示例 前言 一、需使用的组件与软件包及其ENV配置 1、文件系统相关组件与软件包 1.1、DFS 框架 1.2、fal 软件包 1.3、SFUD 组件 2、网络通信相关组件和软件包 ...三、web服务器开发基础 1、HTTP简介
  • 一、环境介绍 ...STM32控制ENC28J60+UIP协议栈创建TCP服务器(WEB服务器),支持浏览器访问完成数据传输。 浏览器可以实时显示温度、时间、可以控制STM32开发板上的LED灯、蜂鸣器。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,871
精华内容 1,548
关键字:

stm32web服务器实现