物联网智慧教室项目8

D8网页web服务器功能开发(STM32)

8.1WebServer功能设计

8.1.1 WebServer需要做什么

(1)当用户访问网址(url)时,加载网页文件

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

8.1.2EasyWebSvr工具介绍

菜单界面

 
设置界面

 
 运行日志

8.1.3EasyWebSvr搭建Web服务器

(1)配置服务器主目录

(2)浏览器输入服务器地址 
127.0.0.1/index.html

(3)分析Web服务器运行日志

 

 8.1.4WebServer功能设计

 (1)文件请求响应


(2) 传感器数据请求响应

  (3) 命令请求响应

 

8.2WebServer主线程实现

8.2.1WebServer代码移植

(1)文件移植

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

(3) 访问STM32服务器,网页图片加载需要多次刷新问题

1、我们webserver是一个单线程任务,http属于短链接//http没有记忆功能,在一次socket通信中能获取多少数据,就只能获取多少数据
2、但是浏览器有缓存,我们使用时只需要多刷新几次就可以了
3、也可以通过在前端增加一些js代码(循环加载前端资源(文件))
4、STM32内存太小了,没有办法做长连接,短连接模式可以实现多个客户端连接

8.2.2 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);
	}
}

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);
}

8.2.3 http解析业务流程

8.3 Http文件请求响应

8.3.1数据结构

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: ";

 http socket.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请求报文消息结构体

8.3.2Respond_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响应
	}	
}

8.3.3Parse_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; //保存下一字符串指针
}

 8.3.4 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);
	}
}

8.4前后台交互方法设计

8.4.1数据结构

typedef struct Web_s{
	struct  Web_s 	*next;   //单链表节点 
	const char      *cmd;    //get&post具体消息内容存放位置
	void            (*function)(void *,void *);//不同响应的处理方法
} WEB_Server_Struct;

8.4.2 交互数据结构封装

 字符串封装
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 消息内容为关闭
数据节点封装
//我们项目中需要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};
交互函数封装
/****************************************************************************************************
 函数原型: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\"}");
	}
}

交互数据结构处理
/*******************************************************************************
* 函数名称:  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;
}

8.5前后台交互实现

8.5.1初始化

//把我们传感器数据获取节点,定义为链表的头结点
#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);
    
    //........

}

8.5.2前台交互解析

/*******************************************************************************
* 函数名称:  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;
	}
}

8.5.3前后台交互响应

/*******************************************************************************
* 函数名称:  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); //释放内存空间
}

 

 

 


 


 

 

 



 


 


        


 

 



 


 

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值