ST MCU Finder
安装免费手机应用,
寻找理想的ST MCU

[信息] STM32移植lwip之建立web服务器

[复制链接]
510|23
 楼主 | 2018-5-13 19:29 | 显示全部楼层 |阅读模式
本篇目标:在之前能ping通pc机的工程基础上搭建web服务器,借鉴官方web服务器的程序与网页,能够用pc机浏览器访问web服务器,并返回设置的网页

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


浏览器请求指令探索

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

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

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

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



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



打开浏览器(谷歌浏览器测试),输入刚才确认的本地ip地址,这里输入192.168.6.104
返回去看看刚才搭建的服务器有什么变化,会发现有接收到的数据,只要重点观察第一行的数据“GET / HTTP/1.1”,这个字符串将会被服务器解析,然后将网页代码返回回去:


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主 | 2018-5-13 19:33 | 显示全部楼层
找到一个官方程序有一个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”
    发现请求中 / 后面一部分的内容不相同,所以服务器只需要解析这一部分的字符串内容,来返回对应的网页数据即可



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主 | 2018-5-13 19:34 | 显示全部楼层
搭建web服务器

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

  • web服务器初始化函数 Http_Server_Init():
  1. void Http_Server_Init(void)
  2. {
  3.     struct tcp_pcb *http_server_pcb;

  4.     /* 为web服务器分配一个tcp_pcb结构体 */
  5.     http_server_pcb = tcp_new();

  6.     /* 绑定本地端号和IP地址 */
  7.     tcp_bind(http_server_pcb, IP_ADDR_ANY, 80);

  8.     /* 监听之前创建的结构体http_server_pcb */
  9.     http_server_pcb = tcp_listen(http_server_pcb);

  10.     /* 初始化结构体接收回调函数 */
  11.     tcp_accept(http_server_pcb, http_server_accept);
  12. }
复制代码

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

  • 接收数据回调函数 tcp_server_accept() :
  1. static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
  2. {
  3.     struct http_state *hs;

  4.     /* 分配内存空间 */
  5.     hs = (struct http_state *)mem_malloc(sizeof(struct http_state));

  6.     if (hs != NULL)
  7.     {
  8.         memset(hs, 0, sizeof(struct http_state));
  9.     }

  10.     /* 确认监听和连接 */
  11.     tcp_arg(pcb, hs);

  12.     /* 配置接收回调函数 */
  13.     tcp_recv(pcb, http_server_recv);

  14.     /* 配置轮询回调函数 */
  15.     tcp_poll(pcb, http_server_poll, 4);

  16.     /* 配置发送回调函数 */
  17.     tcp_sent(pcb, http_sent);

  18.     return ERR_OK;
  19. }
复制代码




 楼主 | 2018-5-13 19:36 | 显示全部楼层

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

  • 接收数据处理函数 http_server_recv() :
  1. static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *http_recv_pbuf, err_t err)
  2. {
  3.     err_t parsed = ERR_ABRT;
  4.     struct http_state *hs = (struct http_state *)arg;

  5.     /* 告诉tcp已经接收到数据 */
  6.     tcp_recved(pcb, http_recv_pbuf->tot_len);

  7.     if (hs->handle == NULL)
  8.     {
  9.          /* 解析接收到的浏览器请求数据 */
  10.          parsed = http_parse_request(&http_recv_pbuf, hs, pcb);
  11.     }

  12.     /* 清空请求字符串 */
  13.     if (parsed != ERR_INPROGRESS)
  14.     {      
  15.         if (hs->req != NULL)
  16.         {
  17.             pbuf_free(hs->req);
  18.             hs->req = NULL;
  19.         }
  20.     }

  21.     if (parsed == ERR_OK)
  22.     {
  23.         /* 发送网页数据 */
  24.         http_send_data(pcb, hs);
  25.     }
  26.     else if (parsed == ERR_ARG)
  27.     {
  28.         /* 关闭连接 */
  29.         close_conn(pcb, hs);
  30.     }

  31.     return ERR_OK;
  32. }
复制代码

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

  • 接收数据解析函数 http_parse_request():
  1. static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb)
  2. {
  3.     char *data;
  4.     char *crlf;
  5.     u16_t data_len;
  6.     struct pbuf *p = *inp;

  7.     char *sp1, *sp2;
  8.     u16_t uri_len;
  9.     char *uri;

  10.     /* 排列字符串 */
  11.     if (hs->req == NULL)
  12.     {
  13.         hs->req = p;
  14.     }
  15.     else
  16.     {
  17.         /* 将多次的请求字符串进行连接排序 */
  18.         pbuf_cat(hs->req, p);
  19.     }

  20.     /* 拷贝输入数据 */
  21.     if (hs->req->next != NULL)
  22.     {
  23.         data_len = hs->req->tot_len;
  24.         pbuf_copy_partial(hs->req, data, data_len, 0);
  25.     }
  26.     else
  27.     {
  28.         data = (char *)p->payload;
  29.         data_len = p->len;
  30.     }

  31.     /* 提取接收到的浏览器字符串,浏览器请求示例:"GET / HTTP/1.1" */
  32.     if (data_len > 7)
  33.     {
  34.         crlf = strstr(data, "\r\n");

  35.         if (crlf != NULL)
  36.         {
  37.             /* 比较前4个字符是否为 "GET " */
  38.             if (strncmp(data, "GET ", 4) == 0)
  39.             {
  40.                 /* sp1指向字符串 "/ HTTP/1.1" */
  41.                 sp1 = (data + 4);
  42.             }

  43.             /* 在sp1字符串中寻找字符" ",sp2指向字符串 " HTTP/1.1" */
  44.             sp2 = strstr(sp1, " ");

  45.             /* uri_len获取sp1字符串首地址到sp2字符串首地址的长度 */
  46.             uri_len = sp2 - (sp1);

  47.             if ((sp2 != 0) && (sp2 >= (sp1)))
  48.             {
  49.                 /* 将解析的字符串赋给uri,并在最后加上结束符\0,
  50.                    uri指向字符串 "/\0" */
  51.                 uri = sp1;
  52.                 *(sp1 - 1) = 0;
  53.                 uri[uri_len] = 0;

  54.                 /* 根据字符串寻找对应网页数据 */
  55.                 return http_find_file(hs, uri, 0);
  56.             }
  57.         }
  58.     }

  59.     return ERR_OK;
  60. }
复制代码

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

  • 读取对应网页数据函数 http_find_file():
  1. static err_t http_find_file(struct http_state *hs, const char *uri, int is_09)
  2. {
  3.     struct fs_file *file = NULL;

  4.     /* 如果字符串为 "/\0",则打开index网页 */
  5.     if((uri[0] == '/') && (uri[1] == 0))
  6.     {
  7.         file = fs_open("/index.html");
  8.         uri = "/index.html";
  9.     }
  10.     else
  11.     {
  12.         /* 如果为其他请求,则打开相应网页 */
  13.         file = fs_open(uri);
  14.     }

  15.     /* 将网页文件数据赋值给http_state结构体,之后发送出去 */
  16.     return http_init_file(hs, file, is_09, uri);
  17. }
复制代码





 楼主 | 2018-5-13 19:37 | 显示全部楼层
小结:此函数就是网页数据读取函数,里面最重要的函数就是 fs_open() 函数了,这个函数在官方建立web服务器工程里的 fs.c 文件里面,这个函数的解析放到后面;

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

上面基本包括了几个重要的函数,当然还有其他的函数,包括发送函数等等,这些函数可以看源代码的注释来理解
 楼主 | 2018-5-13 19:37 | 显示全部楼层
  • http_server.c
  1. #include "lwip/debug.h"
  2. #include "lwip/stats.h"
  3. #include "lwip/tcp.h"
  4. #include "http_server.h"
  5. #include "fs.h"

  6. #include <string.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>

  9. /*
  10. *********************************************************************************************************
  11. *                                            LOCAL TABLES
  12. *********************************************************************************************************
  13. */
  14. static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err);
  15. static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *tcp_recv_pbuf, err_t err);
  16. static err_t http_server_poll(void *arg, struct tcp_pcb *pcb);
  17. static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri);
  18. static err_t http_find_file(struct http_state *hs, const char *uri, int is_09);
  19. static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb);
  20. static u8_t http_send_data(struct tcp_pcb *pcb, struct http_state *hs);
  21. static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len);
  22. static void close_conn(struct tcp_pcb *pcb, struct http_state *hs);

  23. /*
  24. *********************************************************************************************************
  25. *                                      LOCAL FUNCTION PROTOTYPES
  26. *********************************************************************************************************
  27. */

  28. /***
  29. * 函数名称 : Http_Server_Init();
  30. *
  31. * 函数描述 : web服务器初始化;
  32. *
  33. * 传递值    : 无;
  34. *
  35. * 返回值   : 无;
  36. *
  37. **/
  38. void Http_Server_Init(void)
  39. {
  40.     struct tcp_pcb *http_server_pcb;

  41.     /* 为web服务器分配一个tcp_pcb结构体 */
  42.     http_server_pcb = tcp_new();

  43.     /* 绑定本地端号和IP地址 */
  44.     tcp_bind(http_server_pcb, IP_ADDR_ANY, 80);

  45.     /* 监听之前创建的结构体http_server_pcb */
  46.     http_server_pcb = tcp_listen(http_server_pcb);

  47.     /* 初始化结构体接收回调函数 */
  48.     tcp_accept(http_server_pcb, http_server_accept);
  49. }

  50. /***
  51. * 函数名称 : http_server_accept();
  52. *
  53. * 函数描述 : lwip数据接收回调函数,包含对tcp连接的确认,接收回调函数的配置;
  54. *
  55. * 传递值    : *arg, *pcb, err ;
  56. *
  57. * 返回值   : ERR_OK 无错误;
  58. *
  59. **/
  60. static err_t http_server_accept(void *arg, struct tcp_pcb *pcb, err_t err)
  61. {
  62.     struct http_state *hs;

  63.     /* 分配内存空间 */
  64.     hs = (struct http_state *)mem_malloc(sizeof(struct http_state));

  65.     if (hs != NULL)
  66.     {
  67.         memset(hs, 0, sizeof(struct http_state));
  68.     }

  69.     /* 确认监听和连接 */
  70.     tcp_arg(pcb, hs);

  71.     /* 配置接收回调函数 */
  72.     tcp_recv(pcb, http_server_recv);

  73.     /* 配置轮询回调函数 */
  74.     tcp_poll(pcb, http_server_poll, 4);

  75.     /* 配置发送回调函数 */
  76.     tcp_sent(pcb, http_sent);

  77.     return ERR_OK;
  78. }

  79. /***
  80. * 函数名称 : http_server_recv();
  81. *
  82. * 函数描述 : 接受到数据后,根据接收到数据的内容,返回网页;
  83. *
  84. * 传递值    : *arg, *pcb, *http_recv_pbuf, err;
  85. *
  86. * 返回值   : ERR_OK无错误;
  87. *
  88. **/
  89. static err_t http_server_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *http_recv_pbuf, err_t err)
  90. {
  91.     err_t parsed = ERR_ABRT;
  92.     struct http_state *hs = (struct http_state *)arg;

  93.     /* 告诉tcp已经接收到数据 */
  94.     tcp_recved(pcb, http_recv_pbuf->tot_len);

  95.     if (hs->handle == NULL)
  96.     {
  97.         /* 解析接收到的浏览器请求数据 */
  98.         parsed = http_parse_request(&http_recv_pbuf, hs, pcb);
  99.     }

  100.     /* 清空请求字符串 */
  101.     if (parsed != ERR_INPROGRESS)
  102.     {      
  103.         if (hs->req != NULL)
  104.         {
  105.             pbuf_free(hs->req);
  106.             hs->req = NULL;
  107.         }
  108.     }

  109.     if (parsed == ERR_OK)
  110.     {
  111.         /* 发送网页数据 */
  112.         http_send_data(pcb, hs);
  113.     }
  114.     else if (parsed == ERR_ARG)
  115.     {
  116.         /* 关闭连接 */
  117.         close_conn(pcb, hs);
  118.     }

  119.     return ERR_OK;
  120. }

  121. /***
  122. * 函数名称 : http_server_poll();
  123. *
  124. * 函数描述 : 轮询函数;
  125. *
  126. * 传递值    : *arg, *pcb;
  127. *
  128. * 返回值   : ERR_OK无错误;
  129. *
  130. **/
  131. static err_t http_server_poll(void *arg, struct tcp_pcb *pcb)
  132. {
  133.     struct http_state *hs = arg;

  134.     if (hs == NULL)
  135.     {
  136.         close_conn(pcb, hs);
  137.         return ERR_OK;
  138.     }
  139.     else
  140.     {
  141.         hs->retries++;
  142.         if (hs->retries == 4)
  143.         {
  144.             close_conn(pcb, hs);
  145.             return ERR_OK;
  146.         }

  147.         /* 如果连接存在打开的文件,则将会发送剩下的数据;
  148.          * 如果一直没有收到GET请求,那么连接将会立刻关闭 */
  149.         if (hs && (hs->handle))
  150.         {
  151.             if (http_send_data(pcb, hs))
  152.             {
  153.                 tcp_output(pcb);
  154.             }
  155.         }
  156.     }

  157.     return ERR_OK;
  158. }

  159. /***
  160. * 函数名称 : http_parse_request();
  161. *
  162. * 函数描述 : 对接收到的数据进行解析,根据不同的浏览器请求,返回对应的网页数据;
  163. *
  164. * 传递值    : **inp, *hs, *pcb;
  165. *
  166. * 返回值   : ERR_OK无错误;
  167. *
  168. **/
  169. static err_t http_parse_request(struct pbuf **inp, struct http_state *hs, struct tcp_pcb *pcb)
  170. {
  171.     char *data;
  172.     char *crlf;
  173.     u16_t data_len;
  174.     struct pbuf *p = *inp;

  175.     char *sp1, *sp2;
  176.     u16_t uri_len;
  177.     char *uri;

  178.     /* 排列字符串 */
  179.     if (hs->req == NULL)
  180.     {
  181.         hs->req = p;
  182.     }
  183.     else
  184.     {
  185.         /* 将多次的请求字符串进行连接排序 */
  186.         pbuf_cat(hs->req, p);
  187.     }

  188.     /* 拷贝输入数据 */
  189.     if (hs->req->next != NULL)
  190.     {
  191.         data_len = hs->req->tot_len;
  192.         pbuf_copy_partial(hs->req, data, data_len, 0);
  193.     }
  194.     else
  195.     {
  196.         data = (char *)p->payload;
  197.         data_len = p->len;
  198.     }

  199.     /* 提取接收到的浏览器字符串,浏览器请求示例:"GET / HTTP/1.1" */
  200.     if (data_len > 7)
  201.     {
  202.         crlf = strstr(data, "\r\n");
  203.         if (crlf != NULL)
  204.         {
  205.             /* 比较前4个字符是否为 "GET " */
  206.             if (strncmp(data, "GET ", 4) == 0)
  207.             {
  208.                 /* sp1指向字符串 "/ HTTP/1.1" */
  209.                 sp1 = (data + 4);
  210.             }
  211.             /* 在sp1字符串中寻找字符" ",sp2指向字符串 " HTTP/1.1" */
  212.             sp2 = strstr(sp1, " ");
  213.             /* uri_len获取sp1字符串首地址到sp2字符串首地址的长度 */
  214.             uri_len = sp2 - (sp1);

  215.             if ((sp2 != 0) && (sp2 >= (sp1)))
  216.             {
  217.                 /* 将解析的字符串赋给uri,并在最后加上结束符\0,
  218.                    uri指向字符串 "/\0" */
  219.                 uri = sp1;
  220.                 *(sp1 - 1) = 0;
  221.                 uri[uri_len] = 0;

  222.                 /* 根据字符串寻找对应网页数据 */
  223.                 return http_find_file(hs, uri, 0);
  224.                 }
  225.             }
  226.     }

  227.     return ERR_OK;
  228. }

  229. /***
  230. * 函数名称 : http_find_file();
  231. *
  232. * 函数描述 : 对提取的数据进行判断,读取对应的网页数据;
  233. *
  234. * 传递值    : *hs, *uri, is_09;
  235. *
  236. * 返回值   : ERR_OK无错误;
  237. *
  238. **/
  239. static err_t http_find_file(struct http_state *hs, const char *uri, int is_09)
  240. {
  241.     struct fs_file *file = NULL;

  242.     /* 如果字符串为 "/\0",则打开index网页 */
  243.     if((uri[0] == '/') && (uri[1] == 0))
  244.     {
  245.         file = fs_open("/index.html");
  246.         uri = "/index.html";
  247.     }
  248.     else
  249.     {
  250.         /* 如果为其他请求,则打开相应网页 */
  251.         file = fs_open(uri);
  252.     }

  253.     /* 将网页文件数据赋值给http_state结构体,之后发送出去 */
  254.     return http_init_file(hs, file, is_09, uri);
  255. }

  256. /***
  257. * 函数名称 : http_init_file();
  258. *
  259. * 函数描述 : 将要发送的数据保存到http_state结构体当中;
  260. *
  261. * 传递值    : *hs, *file, is_09, *uri;
  262. *
  263. * 返回值   : ERR_OK无错误;
  264. *
  265. **/
  266. static err_t http_init_file(struct http_state *hs, struct fs_file *file, int is_09, const char *uri)
  267. {
  268.     if (file != NULL)
  269.     {
  270.         hs->handle = file;
  271.         /* 将网页数据赋值给http_state */
  272.         hs->file = (char*)file->data;
  273.         /* 将网页长度赋值给http_state */
  274.         hs->left = file->len;
  275.         hs->retries = 0;
  276.     }
  277.     else
  278.     {
  279.         hs->handle = NULL;
  280.         hs->file = NULL;
  281.         hs->left = 0;
  282.         hs->retries = 0;
  283.     }

  284.     return ERR_OK;
  285. }

  286. /***
  287. * 函数名称 : http_send_data();
  288. *
  289. * 函数描述 : 数据发送函数;
  290. *
  291. * 传递值    : *pcb, *hs;
  292. *
  293. * 返回值   : ERR_OK无错误;
  294. *
  295. **/
  296. static u8_t http_send_data(struct tcp_pcb *pcb, struct http_state *hs)
  297. {
  298.     err_t err = ERR_OK;
  299.     u16_t len;
  300.     u8_t data_to_send = 0;

  301.     /* 配置发送数据长度,如果发送数据过长则分批发送 */
  302.     if (tcp_sndbuf(pcb) < hs->left)
  303.     {
  304.         len = tcp_sndbuf(pcb);
  305.     }
  306.     else
  307.     {
  308.         len = (u16_t)hs->left;
  309.     }      

  310.     /* 发送网页数据 */
  311.     err = tcp_write(pcb, hs->file, len, 1);

  312.     if (err == ERR_OK)
  313.     {
  314.         data_to_send = 1;
  315.         hs->file += len;
  316.         hs->left -= len;
  317.     }

  318.     if ((hs->left == 0) && (fs_bytes_left(hs->handle) <= 0))
  319.     {
  320.         /* 关闭连接 */
  321.         close_conn(pcb, hs);
  322.         return 0;
  323.     }

  324.     return data_to_send;
  325. }


  326. /***
  327. * 函数名称 : http_sent();
  328. *
  329. * 函数描述 : 数据已经被发送,并且被远程主机确定;
  330. *
  331. * 传递值    : *arg, *pcb, len;
  332. *
  333. * 返回值   : ERR_OK无错误;
  334. *
  335. **/
  336. static err_t http_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
  337. {
  338.     struct http_state *hs = (struct http_state *)arg;

  339.     if (hs == NULL)
  340.     {
  341.         return ERR_OK;
  342.     }

  343.     hs->retries = 0;

  344.     http_send_data(pcb, hs);

  345.     return ERR_OK;
  346. }

  347. /***
  348. * 函数名称 : close_conn();
  349. *  * 函数描述 : 关闭tcp连接;
  350. *  * 传递值     : *pcb, *hs;
  351. *  * 返回值   : 无;
  352. *  **/
  353. static void close_conn(struct tcp_pcb *pcb, struct http_state *hs)
  354. {
  355.     tcp_arg(pcb, NULL);
  356.     tcp_recv(pcb, NULL);
  357.     tcp_err(pcb, NULL);
  358.     tcp_poll(pcb, NULL, 0);
  359.     tcp_sent(pcb, NULL);

  360.     if (hs != NULL)
  361.     {
  362.         if(hs->handle)
  363.         {
  364.             fs_close(hs->handle);
  365.             hs->handle = NULL;
  366.         }
  367.             mem_free(hs);
  368.     }

  369.     tcp_close(pcb);
  370. }
复制代码



 楼主 | 2018-5-13 19:38 | 显示全部楼层
http_server.h
  1. #ifndef HTTP_SERVER_H
  2. #define HTTP_SERVER_H

  3. /*
  4. *********************************************************************************************************
  5. *                                              INCLUDE FILES
  6. *********************************************************************************************************
  7. */


  8. /*
  9. *********************************************************************************************************
  10. *                                               CONSTANTS
  11. *********************************************************************************************************
  12. */


  13. /*
  14. *********************************************************************************************************
  15. *                                             PERIPH DEFINES
  16. *********************************************************************************************************
  17. */


  18. /*
  19. *********************************************************************************************************
  20. *                                               DATA TYPES
  21. *********************************************************************************************************
  22. */


  23. /*
  24. *********************************************************************************************************
  25. *                                            GLOBAL VARIABLES
  26. *********************************************************************************************************
  27. */

  28. struct http_state {
  29.   struct fs_file *handle;
  30.   char *file;       /* Pointer to first unsent byte in buf. */

  31. #if 1
  32.   struct pbuf *req;
  33. #endif /* LWIP_HTTPD_SUPPORT_REQUESTLIST */

  34. #if 1
  35.   char *buf;        /* File read buffer. */
  36.   int buf_len;      /* Size of file read buffer, buf. */
  37. #endif /* LWIP_HTTPD_SSI || LWIP_HTTPD_DYNAMIC_HEADERS */
  38.   u32_t left;       /* Number of unsent bytes in buf. */
  39.   u8_t retries;
  40. #if 0
  41.   const char *parsed;     /* Pointer to the first unparsed byte in buf. */
  42. #if 1
  43.   const char *tag_started;/* Poitner to the first opening '<' of the tag. */
  44. #endif /* !LWIP_HTTPD_SSI_INCLUDE_TAG */
  45.   const char *tag_end;    /* Pointer to char after the closing '>' of the tag. */
  46.   u32_t parse_left; /* Number of unparsed bytes in buf. */
  47.   u16_t tag_index;   /* Counter used by tag parsing state machine */
  48.   u16_t tag_insert_len; /* Length of insert in string tag_insert */
  49. #if 0
  50.   u16_t tag_part; /* Counter passed to and changed by tag insertion function to insert multiple times */
  51. #endif /* LWIP_HTTPD_SSI_MULTIPART */
  52.   u8_t tag_check;   /* true if we are processing a .shtml file else false */
  53.   u8_t tag_name_len; /* Length of the tag name in string tag_name */
  54.   char tag_name[LWIP_HTTPD_MAX_TAG_NAME_LEN + 1]; /* Last tag name extracted */
  55.   char tag_insert[LWIP_HTTPD_MAX_TAG_INSERT_LEN + 1]; /* Insert string for tag_name */
  56.   enum tag_check_state tag_state; /* State of the tag processor */
  57. #endif /* LWIP_HTTPD_SSI */
  58. #if 0
  59.   char *params[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Params extracted from the request URI */
  60.   char *param_vals[LWIP_HTTPD_MAX_CGI_PARAMETERS]; /* Values for each extracted param */
  61. #endif /* LWIP_HTTPD_CGI */
  62. #if 0
  63.   const char *hdrs[NUM_FILE_HDR_STRINGS]; /* HTTP headers to be sent. */
  64.   u16_t hdr_pos;     /* The position of the first unsent header byte in the
  65.                         current string */
  66.   u16_t hdr_index;   /* The index of the hdr string currently being sent. */
  67. #endif /* LWIP_HTTPD_DYNAMIC_HEADERS */
  68. #if 0
  69.   u32_t time_started;
  70. #endif /* LWIP_HTTPD_TIMING */
  71. #if 0
  72.   u32_t post_content_len_left;
  73. #if 0
  74.   u32_t unrecved_bytes;
  75.   struct tcp_pcb *pcb;
  76.   u8_t no_auto_wnd;
  77. #endif /* LWIP_HTTPD_POST_MANUAL_WND */
  78. #endif /* LWIP_HTTPD_SUPPORT_POST*/
  79. };

  80. /*
  81. *********************************************************************************************************
  82. *                                                 MACRO'S
  83. *********************************************************************************************************
  84. */



  85. /*
  86. *********************************************************************************************************
  87. *                                           FUNCTION PROTOTYPES
  88. *********************************************************************************************************
  89. */

  90. void Http_Server_Init(void);

  91. /*
  92. ********************************************************************************************************
  93. *                                             MODULE END
  94. *********************************************************************************************************
  95. */

  96. #endif /* HTTP_SERVER_H */
复制代码
 楼主 | 2018-5-13 19:39 | 显示全部楼层
官方部分函数解析
读取网页数据文件 fs.c (路径:Project->Standalone->web_server->http):
  1. struct fs_file *fs_open(const char *name)
  2. {
  3.     struct fs_file *file;
  4.     const struct fsdata_file *f;

  5.     /* 分配空间 */
  6.     file = fs_malloc();
  7.     if(file == NULL)
  8.     {
  9.         return NULL;
  10.     }

  11.     for(f = FS_ROOT; f != NULL; f = f->next)
  12.     {
  13.         /* 循环比较,如果输入的请求与网页的头数据一致,则返回该网页数据 */
  14.         if (!strcmp(name, (char *)f->name))
  15.         {
  16.             file->data = (const char *)f->data;
  17.             file->len = f->len;
  18.             file->index = f->len;
  19.             file->pextension = NULL;
  20.             file->http_header_included = f->http_header_included;

  21.             return file;
  22.         }
  23.     }
  24.     fs_free(file);
  25.     return NULL;
  26. }
复制代码


这里关注 f 变量的结构体 fsdata_file,定义在 fsdata.h:
  1. struct fsdata_file
  2. {
  3.     const struct fsdata_file *next;
  4.     const unsigned char *name;
  5.     const unsigned char *data;
  6.     int len;
  7.     u8_t http_header_included;
  8. };
复制代码

结构体中有三个重要的变量*next、*name、*data
而在文件fsdata.c中,拉到最后,发现有几个 fsdata_file 的结构体变量,取其中一个来解析一下:
  1. const struct fsdata_file file__index_html[] =
  2. {
  3.     {
  4.         /* 变量*next,指向下一个要循环比较的数据 */
  5.         file__404_html,
  6.         /* 变量*name,指向数据数组 data__index_html[] */
  7.         data__index_html,
  8.         /* 变量*data,指向数据数组 data__index_html[]12个之后的数据 */
  9.         data__index_html + 12,
  10.         /* 网页数据长度 */
  11.         sizeof(data__index_html) - 12,
  12.         1,
  13.     }
  14. };
复制代码
 楼主 | 2018-5-13 19:41 | 显示全部楼层
变量*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:有部分细节的地方解析的不是很清楚,而且自己也没有想明白,需要再加把劲看一些其他的资料来填补空白,共勉~


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-5-13 19:55 | 显示全部楼层
这个跟使用什么网络模块无关吧
| 2018-5-13 21:02 | 显示全部楼层
不清楚文中提到的CGI是什么,是个函数吗,还是个协议?
| 2018-5-13 21:02 | 显示全部楼层
CGI 是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据库API 与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。几乎所有服务器都支持CGI,可用任何语言编写CGI,包括流行的C、C ++、VB 和Delphi 等。CGI 分为标准CGI 和间接CGI两种。标准CGI 使用命令行参数或环境变量表示服务器的详细请求,服务器与浏览器通信采用标准输入输出方式。间接CGI 又称缓冲CGI,在CGI 程序和CGI 接口之间插入一个缓冲程序,缓冲程序与CGI 接口间用标准输入输出进行通信
| 2018-5-13 21:02 | 显示全部楼层
百度了下,了解了,多谢分享。
| 2018-7-11 15:58 | 显示全部楼层
多谢楼主
| 2018-7-11 22:50 | 显示全部楼层
web服务器
| 2018-7-11 22:51 | 显示全部楼层
有网页版本的吗?
| 2018-7-11 22:51 | 显示全部楼层
lwip支持哪些芯片呢?
| 2018-7-11 22:51 | 显示全部楼层
以前见过他们点亮LED
| 2018-7-11 22:51 | 显示全部楼层
小明的同学 发表于 2018-5-13 19:34
搭建web服务器现在创建一个新的c文件,取名为 http_server.c ,接下来写几个函数来建立web服务器,抽重要的 ...

| 2018-7-11 22:52 | 显示全部楼层
lwip占用多大的资源呢?
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式
我要创建版块 申请成为版主

论坛热帖

快速回复 返回顶部 返回列表