Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3045577
  • 博文数量: 396
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 4209
  • 用 户 组: 普通用户
  • 注册时间: 2016-07-04 13:04
文章分类

全部博文(396)

文章存档

2022年(1)

2021年(2)

2020年(8)

2019年(24)

2018年(135)

2017年(158)

2016年(68)

我的朋友

分类: 嵌入式

2017-06-20 16:18:45

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

材料准备:

  • 基础工程:修改后能ping通pc机的工程()
  • 搭建工程:最终搭建好的web服务器工程()
  • 调试工具:用来调试tcp连接下的数据接收()
  • 测试浏览器:这里使用的是Chrome谷歌浏览器

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);
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

小结:上面函数主要就是为搭建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;
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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

  • 接收数据处理函数 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;
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

小结:函数主要工作将接收到的数据放入 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;
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

小结:这个函数是重要的请求数据解析函数,函数将接收到的字符串(例:“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);
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

小结:此函数就是网页数据读取函数,里面最重要的函数就是 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 
#include 
#include  /*
*********************************************************************************************************
*                                            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);
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 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 */
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115

官方部分函数解析

读取网页数据文件 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;
}
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

这里关注 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;
};
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结构体中有三个重要的变量*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,
    }
};
		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 变量*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:有部分细节的地方解析的不是很清楚,而且自己也没有想明白,需要再加把劲看一些其他的资料来填补空白,共勉~

顶 0
阅读(2981) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~