物联网智慧教室项目(八):网页web服务器功能开发(stm32)
发布时间
阅读量:
阅读量
一、WebServer功能设计
(一)WebServer需要做什么
当用户访问网址(url)时,加载网页文件

当用户点击控制按钮,传感器定时刷新与服务器进行交互

(二)EasyWebSvr工具介绍
菜单界面

设置界面

日志

(三)EasyWebSvr搭建Web服务器
配置服务器主目录

浏览器输入服务器地址
0.0.1/index.html
url

分析Web服务器日志

(四)WebServer功能设计
文件请求响应
BrowserWebServerFileSystemGET /index.htmlopen(index.html)index.htmlResponse index.htmlBrowserWebServerFileSystem
传感器数据请求响应
BrowserWebServerSensorTaskGET /DATA/SensorRead SensorDataSensorDataResponse SensorDataBrowserWebServerSensorTask
命令请求响应
BrowserWebServerCMDTaskPOST /CMD/LightSend CmdCmd StatusResponse Cmd StatusBrowserWebServerCMDTask
二、WebServer主线程实现
(一)WebServer代码移植
文件移植
智慧教室项目实战\day08\03-WebServer移植文件
c

把网页文件拷贝到SD卡中
拷贝文件到SD卡"根目录下"
2.开发板断电插入SD卡
3.烧录程序看现象(浏览器输入192.168.1.7)
c

访问STM32服务器,网页图片加载需要多次刷新问题
1、我们webserver是一个单线程任务,http属于短链接//http没有记忆功能,在一次socket通信中能获取多少数据,就只能获取多少数据
2、但是浏览器有缓存,我们使用时只需要多刷新几次就可以了
3、也可以通过在前端增加一些js代码(循环加载前端资源(文件))
4、STM32内存太小了,没有办法做长连接,短连接模式可以实现多个客户端连接
c
(二)WebServer主线程实现
http_server_socket_thread
/** * @brief http server thread
* @param arg: pointer on argument(not used here)
* @retval None
*/
void http_server_socket_thread(void *p_arg)
{
int sock, newconn, size;
struct sockaddr_in address, remotehost;
/* create a TCP socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("http_server can not create socket");
return;
}
/* bind to port 80 at any interface */
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&address, sizeof(address)) == -1)
{
printf("http_server can not bind socket");
return;
}
/* listen for incoming connections (TCP listen backlog = 5) */
listen(sock, 5);
size = sizeof(remotehost);
/*先不关心,但是很重要
printf("\r\n--------------Web_Server_Task----------------\r\n");
WEB_Service_Registration(&CONTROL_LIGHT_CMD_POST);
WEB_Service_Registration(&CONTROL_FAN_CMD_POST);
WEB_Service_Registration(&CONTROL_ALARM_CMD_POST);
*/
// 循环等待客户端的接入
while (1)
{
//等待客户端接入
newconn = accept(sock, (struct sockaddr *)&remotehost, (socklen_t *)&size);
//针对客户端做服务处理 请求-->响应
http_server_serve(newconn);
}
}
c

http_server_serve
/** * @brief serve tcp connection
* @param conn: connection socket
* @retval None
*/
void http_server_serve(int conn)
{
int ret;
/* Read in the request */
ret = read(conn, (unsigned char *)Request_Buf, 1500);
if (ret < 0)
return;
else
{
//把请求内容最后一个字节填充\0,以后直接用字符串解析
*(Request_Buf + ret) = 0;
#ifdef WEB_DEBUG
printf("\r\nWEB服务器,接收请求,内容:\r\n%s\r\n", (const char *)Request_Buf);
#endif
//请求响应代码
Respond_Http_Request(conn, (char *)Request_Buf);
}
//关闭socket 这就一个短链接的实现
close(conn);
}
c

(三)http解析业务流程
请求解析
方法类型
ERROR
GET
POST
错误响应
GET类型
数据读取响应
文件读取响应
POST类型
命令下发响应
文件写入响应
三、Http文件请求响应
(一)数据结构
http文件请求响应首部封装
#define HOMEPAGE_DEFAULT "index.html"
#define INVALID_CMD "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: 16\r\n\r\ninvalid cmd!"
#define POST_REQUEST_OK "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: text/css\r\nCache-control: no-cache\r\nExpires: Thu, 15 Apr 2000 20:00:00 GMT\r\nContent-Length: 18\r\n\r\nPOST Successfully!"
#define POST_REQUEST_FAIL "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: text/css\r\nCache-control: no-cache\r\nExpires: Thu, 15 Apr 2000 20:00:00 GMT\r\nContent-Length: 13\r\n\r\nPOST Failure!"
#define RETURN_cmd_OK "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nCache-Control: no-cache, no-store, max-age=0\r\nExpires: 1L\r\nConnection: close\r\nContent-Length: "
/* html文件请求错误响应 */
const char ERROR_HTML_PAGE[] = "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: 78\r\n\r\n<HTML>\r\n<BODY>\r\nSorry, the page you requested was not found.\r\n</BODY>\r\n</HTML>\r\n\0";
/* 数据命令请求错误响应 */
const char ERROR_REQUEST_PAGE[] = "HTTP/1.0 500 Pafe Not Found\r\nConnection: close\r\nContent-Type: text/html\r\nContent-Length: 50\r\n\r\n<HTML>\r\n<BODY>\r\nInvalid request.\r\n</BODY>\r\n</HTML>\r\n\0";
/* Response header for HTML*/
const char RES_HTMLHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: text/html\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for TEXT */
const char RES_TEXTHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: text/plain\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for GIF */
const char RES_GIFHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: image/gif\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for JPEG */
const char RES_JPEGHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: image/jpeg\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for MPEG */
const char RES_PNGHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: image/png\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for MP3 */
const char RES_MP3HEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: audio/mpeg\r\nContent-Range: bytes 0-40123/40124\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for JS */
const char RES_JSHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\ncontent-type:application/x-javascript\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for ICO */
const char RES_ICOHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: image/x-icon\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for CSS */
const char RES_CSSHEAD_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: text/css\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
/* Response head for APP */
const char RES_APP_OK[] = "HTTP/1.0 200 OK\r\nServer: WFM-Control\r\nConnection: close\r\nContent-Type: application/octet-stream\r\nCache-control: max-age=315360000000\r\nExpires: Thu, 15 Apr 2100 20:00:00 GMT\r\nContent-Length: ";
c

http消息结构体
typedef struct
{
char Method; //请求方法: GET 、HEAD 、POST
char *URL; //URL
char FileType; //文件类型
char *Post_Data; //POST数据
unsigned int Content_Length; //POST数据的长度
} Http_Request_MSG_Type; //定义HTTP请求报文消息结构体
c
(二)Respond_Http_Request
/******************************************************************************* * 函数名称: void Respond_Http_Request(SOCKET ch ,char* Request_Msg)
* 函数说明: 响应HTTP请求
* 输入参数: socket 端口:ch ;HTTP请求数据包指针 :Request_Msg
* 返回参数: 无
*******************************************************************************/
void Respond_Http_Request(char ch, char *Request_Msg)
{
//创建http解析结构体
Http_Request_MSG_Type Http_Request_MSG;
//定义两个指针,用于解析数据记录跟踪
char *thisstart = NULL;
char *nextstart = NULL;
//缓冲数据指针
char *buf;
//缓存数据长度
int length = 0;
//进行数据解析,解析完毕后,会填充Http_Request_MSG
Parse_Http_Request(Request_Msg, &Http_Request_MSG); //解析HTTP请求类型
switch (Http_Request_MSG.Method)
{
case METHOD_ERR:
//把错误响应返回
write(ch, (const unsigned char *)ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
break;
case METHOD_HEAD:
case METHOD_GET:
if (strstr((const char *)Http_Request_MSG.URL, "/DATA/")) //判断是否是通讯指令,并解析指令帧
{
//传感器数据请求响应
}
else
{
//响应文件请求
Send_Response_File(ch, &Http_Request_MSG); //发送请求的文件
}
break;
case METHOD_POST:
//post响应
}
}
c

(三)Parse_Http_Request
/******************************************************************************* * 函数名称: void Parse_Http_Request(char * Request_Msg ,Http_Request_MSG_Type *Http_Request_MSG)
* 函数说明: 解析HTTP请求
* 输入参数: 请求数据包指针:Request_Msg ; 请求信息类型 :Http_Request_MSG
* 返回参数: 无
*******************************************************************************/
void Parse_Http_Request(char *Request_Msg, Http_Request_MSG_Type *Http_Request_MSG)
{
char *thisstart = NULL;
char *nextstart = NULL;
thisstart = strtok_r(Request_Msg, " ", &nextstart);
if (thisstart == NULL)
{
Http_Request_MSG->Method = METHOD_ERR;
Http_Request_MSG->URL = NULL;
return;
}
//解析请求所用的方法:GET,HEAD,POST
if (!strcmp(thisstart, "GET") || !strcmp(thisstart, "get"))
{
Http_Request_MSG->Method = METHOD_GET;
}
else if (!strcmp(thisstart, "HEAD") || !strcmp(thisstart, "head"))
{
Http_Request_MSG->Method = METHOD_HEAD;
}
else if (!strcmp(thisstart, "POST") || !strcmp(thisstart, "post"))
{
Http_Request_MSG->Method = METHOD_POST;
}
else
{
Http_Request_MSG->Method = METHOD_ERR;
Http_Request_MSG->URL = NULL;
return;
}
if (nextstart == NULL)
{
Http_Request_MSG->Method = METHOD_ERR;
Http_Request_MSG->URL = NULL;
return;
}
Http_Request_MSG->URL = strtok_r(NULL, " ?", &nextstart); //解析URL
if (Http_Request_MSG->URL[0] == '/' && Http_Request_MSG->URL[1] == '\0') //如果url仅是一个“/”,则默认为主页
{
Http_Request_MSG->URL = HOMEPAGE_DEFAULT; //设置默认页
}
Http_Request_MSG->Post_Data = nextstart; //保存下一字符串指针
}
c

(四)Send_Response_File
/******************************************************************************* * 函数名称: void Send_Response_File(SOCKET ch,Http_Request_MSG_Type *Http_Request_MSG)
* 函数说明: 回应请求的文件
* 输入参数: SOCKET 通道号 ,Http_Request_MSG_Type 文件类型信息
* 返回参数: 无
*******************************************************************************/
void Send_Response_File(char ch, Http_Request_MSG_Type *Http_Request_MSG)
{
FRESULT res;
FIL *f;
unsigned int bytes_ret;
unsigned char *buf;
unsigned char *buf1;
uint32_t fSize;
f = (FIL *)pvPortMalloc(sizeof(FIL)); //开辟内存空间
buf = (unsigned char *)pvPortMalloc(1500); //开辟内存空间
buf1 = (unsigned char *)pvPortMalloc(1500); //开辟内存空间
if (f == NULL || buf == NULL || buf1 == NULL)
{
printf("内存分配失败\r\n");
vPortFree(f); //释放内存空间
vPortFree(buf); //释放内存空间
vPortFree(buf1); //释放内存空间
write(ch, (const unsigned char *)ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
return;
}
//打开文件
res = f_open(f, Http_Request_MSG->URL, FA_OPEN_EXISTING | FA_READ);
//获取文件大小
fSize = f_size(f);
printf("http file size is %d\r\n", fSize);
if (res == FR_OK)
{
Http_Request_MSG->FileType = Parse_URL_File_Type(Http_Request_MSG->URL); //分析请求URL中包含的文件的文件类型
Make_http_response_head((char *)buf, Http_Request_MSG->FileType, fSize); //生成HTTP报文
//响应首部
write(ch, (const unsigned char *)buf, strlen((const char *)buf));
//响应主题------- 文件
while (1)
{
//读取文件
res = f_read(f, buf, 1500, &bytes_ret);
//读取文件错误
if (res != FR_OK)
{
printf("读取文件失败!文件名:%s,错误代码:0x%02x 文件大小:%d\r\n", (const char *)Http_Request_MSG->URL, res, bytes_ret);
SD_initialize(0);
break;
}
//读取文件内容为空
if (bytes_ret == 0)
break;
//读取文件正确,写回socket
write(ch, (const unsigned char *)buf, bytes_ret);
//继续读取文件
res = f_read(f, buf1, 1500, &bytes_ret);
if (res != FR_OK)
{
printf("读取文件失败!文件名:%s,错误代码:0x%02x 文件大小:%d\r\n", (const char *)Http_Request_MSG->URL, res, bytes_ret);
SD_initialize(0);
break;
}
if (bytes_ret == 0)
break;
//读取文件正确写回
write(ch, (const unsigned char *)buf1, bytes_ret);
}
//关闭文件
f_close(f);
vPortFree(f); //释放内存空间
vPortFree(buf); //释放内存空间
vPortFree(buf1); //释放内存空间
}
else //文件打开错误
{
f_close(f);
vPortFree(f); //释放内存空间
vPortFree(buf); //释放内存空间
vPortFree(buf1); //释放内存空间
//写入错误请求响应
write(ch, (const unsigned char *)ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
printf("打开文件失败!文件名:%s,错误代码:0x%02x\r\n", (const char *)Http_Request_MSG->URL, res);
SD_initialize(0);
}
}
c

四、前后台交互方法设计
(一)数据结构
typedef struct Web_s{
struct Web_s *next; //单链表节点
const char *cmd; //get&post具体消息内容存放位置
void (*function)(void *,void *);//不同响应的处理方法
} WEB_Server_Struct;
c
(二)交互数据结构封装
字符串封装
const char Sensor[] = "Sensor"; // get方法内消息 /DATA/Sensor 用来解析此请求
const char Light[] = "Light";//post方法内消息 /CMD/Light 用来解析此请求
const char Fan[] = "Fan";//post方法内消息 /CMD/Fan 用来解析此请求
const char Alarm[] = "Alarm";//post方法内消息 /CMD/Alarm 用来解析此请求
const char On[] = "On";//post方法内消息 /CMD/xxx 消息内容为开启
const char Off[] = "Off";//post方法内消息 /CMD/xxx 消息内容为关闭
c
数据节点封装
//我们项目中需要4中交互,传感器数据获取,开关灯,风扇,报警器,定义下面结构体
WEB_Server_Struct SENSOR_WEB_DATA_GET = {NULL, Sensor, Get_SensorValue};
WEB_Server_Struct CONTROL_LIGHT_CMD_POST = {NULL, Light, Post_Cmd_Light};
WEB_Server_Struct CONTROL_FAN_CMD_POST = {NULL, Fan, Post_Cmd_Fan};
WEB_Server_Struct CONTROL_ALARM_CMD_POST = {NULL, Alarm, Post_Cmd_Alarm};
c
交互函数封装
/****************************************************************************************************
函数原型:void Get_SensorValue(void *buffer,void *value)
入口参数:发送缓冲区指针,设定值指针
出口参数:无
函数功能:登录验证
****************************************************************************************************/
static void Get_SensorValue(void *buffer, void *value)
{
//把传感器数据填充到我们的buffer里面,之后进行响应就ok了
sprintf(buffer, "{\"temperature\":\"%d\",\"humidity\":\"%d\",\"light\":\"10021.1\"}", SensorData[0], SensorData[1]);
}
/****************************************************************************************************
函数原型:void Post_Cmd_Light(void *buffer,void *value)
入口参数:发送缓冲区指针,设定值指针
出口参数:无
函数功能:登录验证
****************************************************************************************************/
static void Post_Cmd_Light(void *buffer, void *value)
{
//判断value是on还是off
if (strstr(value, On))
{
//响应状态为on Status:On
sprintf(buffer, "{\"Status\":\"On\"}");
HAL_GPIO_WritePin(GPIOF, D6_Pin | D7_Pin | D8_Pin | D9_Pin, GPIO_PIN_RESET);
}
else if (strstr(value, Off))
{
//响应状态为Off Status:Off
sprintf(buffer, "{\"Status\":\"Off\"}");
HAL_GPIO_WritePin(GPIOF, D6_Pin | D7_Pin | D8_Pin | D9_Pin, GPIO_PIN_SET);
}
else
{
//响应错误
sprintf(buffer, "{\"Status\":\"Error\"}");
}
}
/****************************************************************************************************
函数原型:void Post_Cmd_Fan(void *buffer,void *value)
入口参数:发送缓冲区指针,设定值指针
出口参数:无
函数功能:登录验证
****************************************************************************************************/
static void Post_Cmd_Fan(void *buffer, void *value)
{
if (strstr(value, On))
{
sprintf(buffer, "{\"Status\":\"On\"}");
//风扇控制 ----- 后面zigbee项目讲解
FanControl(0x01);
}
else if (strstr(value, Off))
{
sprintf(buffer, "{\"Status\":\"Off\"}");
FanControl(0x0);
}
else
{
sprintf(buffer, "{\"Status\":\"Error\"}");
}
}
/****************************************************************************************************
函数原型:void Post_Cmd_Alarm(void *buffer,void *value)
入口参数:发送缓冲区指针,设定值指针
出口参数:无
函数功能:登录验证
****************************************************************************************************/
static void Post_Cmd_Alarm(void *buffer, void *value)
{
if (strstr(value, On))
{
sprintf(buffer, "{\"Status\":\"On\"}");
HAL_GPIO_WritePin(BUZ_GPIO_Port, BUZ_Pin, GPIO_PIN_SET);
}
else if (strstr(value, Off))
{
sprintf(buffer, "{\"Status\":\"Off\"}");
HAL_GPIO_WritePin(BUZ_GPIO_Port, BUZ_Pin, GPIO_PIN_RESET);
}
else
{
sprintf(buffer, "{\"Status\":\"Error\"}");
}
}
c

(三)交互数据结构处理
/******************************************************************************* * 函数名称: void WEB_Service_Registration(WEB_Server_Struct *next)
* 函数说明: WEB数据服务注册,与应用程序间的映射建立。
* 输入参数: 相应应用程序的链表类型指针
* 返回参数: 无
*******************************************************************************/
void WEB_Service_Registration(WEB_Server_Struct *next)
{
//首先获取头结点
WEB_Server_Struct *f = WEB_Registry_Head;
//传入的节点下一个指向空
next->next = NULL;
//遍历找到空节点位置
while (f->next != NULL)
{
f = f->next;
}
//把节点插入到链表中
f->next = next;
}
/******************************************************************************* * 函数名称: char Search_match_the_analytical_method(const char *cmd , char *body_Buf)
* 函数说明: 根据命令搜寻匹配解析方法
* 输入参数: WBE网页发来的命令
* 返回参数: 搜寻匹配成功返回0 ; 未找到匹配命令返回1
*******************************************************************************/
char Search_match_the_analytical_method(const char *cmd, char *body_Buf)
{
//找到头结点
WEB_Server_Struct *f = WEB_Registry_Head;
char *p;
printf("CMD:\r\n");
printf("%s\r\n", cmd);
for (f = WEB_Registry_Head; f != NULL; f = f->next)
{
//判断cmd是否在链表内
p = strstr(cmd, f->cmd);
if (p != NULL)
{
//获取命令数据
p = (char *)cmd;
//获取命令 value指针
p = p + strlen(f->cmd) + 1;
//进行响应处理
f->function(body_Buf, p); //此函数不可重入,所以停止任务调度
return 1;
}
}
return 0;
}
/******************************************************************************* * 函数名称: char POST_Search_match_the_analytical_method(char *cmd,char *dat)
* 函数说明: POST方式下获取的命令和数据,根据命令搜寻匹配解析方法
* 输入参数: WBE网页POST发来的命令和数据包
* 返回参数: 搜寻匹配成功返回0 ; 未找到匹配命令返回1
*******************************************************************************/
char POST_Search_match_the_analytical_method(char *cmd, char *body_buf, char *dat)
{
WEB_Server_Struct *f = WEB_Registry_Head;
for (f = WEB_Registry_Head; f != NULL; f = f->next)
{
if (strstr(cmd, f->cmd))
{
//dat在上层应用获取到,直接传入value值
f->function((char *)body_buf, dat); //此函数不可重入,所以停止任务调度
return 1;
}
}
return 0;
}
c

五、前后台交互实现
(一)初始化
//把我们传感器数据获取节点,定义为链表的头结点
#define WEB_Registry_Head &SENSOR_WEB_DATA_GET
/** * @brief http server thread
* @param arg: pointer on argument(not used here)
* @retval None
*/
void http_server_socket_thread(void *p_arg)
{
//.......
printf("\r\n--------------Web_Server_Task----------------\r\n");
//把CMD相关的节点插入到链表当中
WEB_Service_Registration(&CONTROL_LIGHT_CMD_POST);
WEB_Service_Registration(&CONTROL_FAN_CMD_POST);
WEB_Service_Registration(&CONTROL_ALARM_CMD_POST);
//........
}
c

(二)前台交互解析
/******************************************************************************* * 函数名称: void Respond_Http_Request(SOCKET ch ,char* Request_Msg)
* 函数说明: 响应HTTP请求
* 输入参数: socket 端口:ch ;HTTP请求数据包指针 :Request_Msg
* 返回参数: 无
*******************************************************************************/
void Respond_Http_Request(char ch, char *Request_Msg)
{
Http_Request_MSG_Type Http_Request_MSG;
char *thisstart = NULL;
char *nextstart = NULL;
char *buf;
int length = 0;
Parse_Http_Request(Request_Msg, &Http_Request_MSG); //解析HTTP请求类型
switch (Http_Request_MSG.Method)
{
case METHOD_ERR:
write(ch, (const unsigned char *)ERROR_REQUEST_PAGE, sizeof(ERROR_REQUEST_PAGE));
break;
case METHOD_HEAD:
case METHOD_GET:
if (strstr((const char *)Http_Request_MSG.URL, "/DATA/")) //判断是否是通讯指令,并解析指令帧
{
char *buf;
buf = (char *)pvPortMalloc(128);
if (Search_match_the_analytical_method((const char *)(Http_Request_MSG.URL + 6), buf)) //匹配解析方法,匹配成功,执行相应动作
{
Send_Web_Service_Data(ch, buf); //回应请求的数据
}
else
{
write(ch, INVALID_CMD, sizeof(INVALID_CMD)); //无此指令,回应无效请求
}
vPortFree(buf); //释放内存空间
}
else
{
//发送请求的文件
}
break;
case METHOD_POST:
//获取POST内容
thisstart = strstr(Http_Request_MSG.Post_Data, "Content-Length:");
if (thisstart != NULL)
{
Http_Request_MSG.Content_Length = atoi(thisstart + 15); //获取POST内容的大小
}
thisstart = strstr(thisstart, "\r\n\r\n") + 4;
//Post_Data = "On"/"Off"
Http_Request_MSG.Post_Data = thisstart;
length = strlen(thisstart); //修改bug
//解析POST内存
if (strstr((const char *)Http_Request_MSG.URL, "/CMD/")) //判断是否是通讯指令,并解析指令帧
{
//本次接收的数据尾指针获取
nextstart = thisstart + length;
//nextstart - thisstart 本次socket接收数据的内容
// Content_Length 是这次post body数据的长度
while ((nextstart - thisstart) < Http_Request_MSG.Content_Length) //可能数据量很大,获取POST完整内容
{
//后续还有数据,再调用read进行读取
length = read(ch, (unsigned char *)nextstart, 1500);
if (length > 0)
//把数据尾指针更新
nextstart += length;
//没有后续数据
else if (length < 0)
break;
}
//填充\0保证一个完整字符串
*nextstart = '\0';
//更新整个post下发的长度字段
Http_Request_MSG.Content_Length = nextstart - thisstart;
//解析数据包
buf = (char *)pvPortMalloc(128); //开辟内存空间--原先2048
if (POST_Search_match_the_analytical_method(Http_Request_MSG.URL + 5, buf, Http_Request_MSG.Post_Data))
{
Send_Web_Service_Data(ch, buf);
}
else
{
write(ch, INVALID_CMD, sizeof(INVALID_CMD));
}
vPortFree(buf);
}
else
{
//下发文件
}
break;
default:
break;
}
}
c

(三)前后台交互响应
/******************************************************************************* * 函数名称: void Send_Web_Service_Data(SOCKET ch,char*body_buf)
* 函数说明: 发送Web回应数据包
* 输入参数: SOCKET 通道号 ,body_buf 要发送的数据包
* 返回参数: 无
*******************************************************************************/
void Send_Web_Service_Data(char ch, char *body_buf)
{
char *buf;
char *P_index;
short length = 0;
//获取body长度
length = strlen((const char *)body_buf);
//开辟内存空间
buf = (char *)pvPortMalloc(length + sizeof(RETURN_cmd_OK) + 50); //开辟内存空间
sprintf(buf, "%s%u\r\n\r\n%s", RETURN_cmd_OK, length, body_buf);
length = strlen((const char *)buf);
P_index = buf;
//写入socket按照1500个字节进行传输
while (length > 1500)
{
//如果write单次发送的长度太大,lwip占用动态内存比较多,这是时候,我们需要手动分片
write(ch, (const unsigned char *)P_index, 1500);
length -= 1500;
P_index += 1500;
}
if (length > 0)
{
write(ch, (const unsigned char *)P_index, length);
}
vPortFree(buf); //释放内存空间
}
c

全部评论 (0)
还没有任何评论哟~
