LWIP+HTTP协议实现,待完善

扣扣技术交流群:460189483

LWIP协议栈如何实现POST功能的,很多不正确的地方希望读者指出,及时改正。现有的LWIP协议栈都只实现GET,未实现POST功能。这里先说说POST的特点和功能:

前文提到,GET和POST是两种最常用的HTTP请求方法。我们日常生活中“打开网页”的操作相当于“使用GET方法获取服务器资源”,而“上传附件”、“提交表格”等操作相当于“使用POST将本地资源提交给服务器”。似乎GET方法和POST方法的区别就是一个用于“索取”,一个用于“递交”。这么说也对也不对,实际使用中确实有部分程序这样使用了,但是HTTP协议设计时可不是这样考虑的,下面的表格简单对比了两者的一些差异。

                                             GET 方法 :                                     POST 方法:

可传递数据类型        ASCII文本(汉字有专门的方法转换)       不限,支持二进制文件

可传送的数据量        有限制(2048字节减去URL长度)            无限制

内容编码类型           application/x-www-form-urlencoded    application/x-www-form-urlencoded                                                                                                                  或multipart/form-data

后退/刷新                 回退后再次前进或刷新不会通知用户         数据会被再次提交

历史记录                  浏览器会记录全部内容                             浏览器只记录接收POST内容的URL但不                                                                                                                    记录POST的具体内容

典型应用                  获取服务器上的资源,如下载                   向服务器添加资源,如上传附件

至于POST支持上传的内容编码就不详说了,首先我们还得从浏览器客户端说起,浏览器中提交的表单时方式采用POST,如图:LWIP协议栈实现POST方法此处提交的表单是一个用户自己选择的二进制文件,在与WEB服务器通信时,点击上传新固件后,浏览器会发出请求,并接着随着请求上传文件数据。这里有个细节的问题,不同的浏览器和不同的提交方式,会导致向服务器提交时发送的第一个和第二个数据包不同:(第二种情况是另一个同事遇到的情况,没具体查看)

第一种情况:第一个包只包含数据包包头,第二个包包含文件名和文件数据,以后的包全是上传的文件数据。

第二种情况:第一个包包含数据包包头和文件名,以及文件数据,以后的包全是文件数据。

笔者是第一种情况,但两种情况的处理方式可根据后面的论述同样处理,接着来看用wireshark抓包工具抓到的第一二个数据包和最后一个数据包:(以下是原始数据和复制到记事本后的数据)

第一个数据包:
LWIP协议栈实现POST方法
LWIP协议栈实现POST方法

第二个数据包:
LWIP协议栈实现POST方法

LWIP协议栈实现POST方法

最后一个数据包:LWIP协议栈实现POST方法

 

从第一个包中可以看出接着要继续接受的文件的大小(437116字节),接受的数据编码方式,以及后文提到的"boundary=......."等。

在服务器中要使用到POST功能时,首先要打开功能开关,httpd.h中提到一个可以打开POST功能的宏开关需要我们在合适的位置打开它。服务器端的POST实现的流程如下:

LWIP协议栈实现POST方法
 

服务器端在接收到数据后LWIP中已经实现了每个数据包包头(这里仅指数据包最前面包含src address和dest address等信息的前54个字节)的处理,其后的数据(第一个包的POST处开始后面的数据,以及第二个包中----webki......处开始后面的数据)都存放在一个存放数据的pbuf的结构体中,而且在每次接收一个数据包并处理后,需要对pbuf进行释放,后面的数据才能继续正常接收。对这些数据做出处理时,直接从pbuf中提取出来用即可。  

 由于第一个包包含了内容是是GET还是POST方法提交的请求,lwip已经实现了对第一个包的解析接口函数http_post_request (),当然也可以加入一些自己需要的功能代码(如后文将要提到的提取第一个包中的boundary)。从第二个包开始后的数据的接收,提取,处理,LWIP协议栈提供了三个标准的POST处理接口:

  1. httpd_post_begin();
  2. httpd_post_receive_data();
  3. httpd_post_finished();

这三个接口用于接收前的准备,接收数据,接收后的处理。但LWIP协议栈并未实现这三个接口,下面我们就可以根据我们抓到的数据包的具体内容来实现这三个标准接口了。

有一个注意的地方,要获取上传的文件数据,需要用到第一个包中的一段标志字符串(每次上传同样的一个文件,这个字符串会不一样),即图中的“boundary=----WebKitFormBoundaryWi9cGoLBSzBxZqzi”,这个标志字符串用于标记接受的数据中文件的开始和结束位置,这串字符串的获取需要在LWIP协议栈中已经实现的接口http_post_request ()中加入一段获取这段字符串的代码,并把这段标记性的字符串保存起来。如下为截取的一段获取这段标志字符串的一些代码:
LWIP协议栈实现POST方法

获得标志字符串后,就可以从pbuf中提取文件数据了。下面就直接贴出这三个接口的源码了:(在贴在这里之前,查了很多网上资料都简单的实现几行代码,没有针对实际通信过程时的处理和数据解析的具体过程)

一. httpd_post_begin();
LWIP协议栈实现POST方法

此接口中记录了后期需要处理的CGI函数,在httpd_post_finished()中进行调用和处理。

二. httpd_post_receive_data();

这是针对第二个包的数据接收处理:
LWIP协议栈实现POST方法

这是针对第三个包以及以后的数据接收:
LWIP协议栈实现POST方法

 

在这个接口中,可以将收到的数据用于自己的需求。这里笔者需要实现的功能是将读取的数据写入flash中,自己编写了一个些数据到flash的函数,因为需要八字节对齐才能写入flash,而每次接收的数据大小不满足八字节的倍数,前后两个包需要进行融合,用了一个下午和一个晚上的时间才调试正确出来。

三. httpd_post_finished();LWIP协议栈实现POST方法

这个接口函数里面,可以调用前期记录的一些CGI。

到此实现了POST的功能和数据接收以及处理了。

 

通过封装HTTP头,向服务器发送GET请求,通过GET请求上传数据,并获取返回值

#include <string.h>
#include <stdio.h>
#include <stdint.h>

char text[30];
char temp[10];

/**
  * @brief   组HTTP GET报文
  * @param   pkt   报文缓存指针
  * @param   key   key唯一识别码
  *	@param 	 data  数据
  */
uint32_t HTTP_GETPkt(char *pkt, const char *key, int data)
{
  *pkt = 0;
  memset(temp, 0, 10);
  memset(text, 0, 30);

  // 组装GET请求字符串
  sprintf(temp,"%d", data);
  strcat(text, "auth_key=");
  strcat(text, key);
  strcat(text, "&");
  strcat(text, "data=");
  strcat(text, temp);
  
  // 向服务器发送GET请求的文件地址
  strcat(pkt, "GET /getinfo.php?");
  strcat(pkt, text);
  // 使用1.1版本HTTP
  strcat(pkt, " HTTP/1.1\r\n");
  // 服务器所在地址,这是自己电脑通过无线局域网建立的服务器
  // IP:192.168.1.108,PORT:8888
  strcat(pkt, "Host: 192.168.1.108:8888\r\n");
  // 保持连接
  strcat(pkt, "Connection: Keep-Alive\r\n");
  // 不使用缓存
  strcat(pkt, "Cache-Control: no-cache\r\n");
  strcat(pkt, "\r\n\r\n");
  
  return strlen(pkt);
}

通过调用HTTP_GETPkt()函数并传入指定参数,就可以通过局域网上传数据到自己搭建的服务器,当应用于公网时,只需要把IP和PORT做相应修改即可 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要使用STM32CubeMX、LAN8720、LwIP和FreeRTOS实现网络通信,需要注意以下几点。 首先,STM32CubeMX是一个图形化配置工具,用于为STM32微控制器生成初始化的代码框架。我们可以通过选择所需的外设(如以太网控制器)和配置参数来生成代码。 其次,LAN8720是一个用于实现以太网通信的PHY(物理层)芯片,负责将数据从媒介访问控制层(MAC层)转换为物理传输信号。 接下来,LwIP(Lightweight IP)是一个轻量级的网络协议栈,用于实现TCP/IP协议。我们需要将LwIP集成到项目中,并配置好网络参数,如IP地址、子网掩码和网关。 最后,FreeRTOS是一个流行的实时操作系统,用于管理任务调度和资源管理。我们可以将网络通信任务添加到FreeRTOS的任务列表中,并通过队列和信号量等机制进行任务间通信和同步。 总体实现步骤如下: 1. 使用STM32CubeMX选择并配置以太网控制器和PHY,并生成初始化代码。 2. 配置LwIP的网络参数,如IP地址、子网掩码和网关。 3. 将LwIP集成到项目中,包括源代码和相应的头文件。 4. 添加网络通信任务到FreeRTOS的任务列表中。 5. 在网络任务中,使用LwIP提供的API进行网络初始化、连接设置以及数据收发等操作。 6. 通过使用队列或信号量等机制,实现不同任务间的数据共享和同步。 7. 在主函数中初始化FreeRTOS,并启动任务调度器。 通过以上步骤,我们可以利用STM32CubeMX、LAN8720、LwIP和FreeRTOS实现网络通信功能。这样我们就可以在STM32微控制器上实现网络连接、数据传输等网络应用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值