
ESP8266学习笔记(10)——官方WebServer
发布日期:2021-05-06 23:36:49
浏览次数:8
分类:技术文章
本文共 26326 字,大约阅读时间需要 87 分钟。
一、背景
Post请求和Get请求:
二、流程
① 初始化WebServer,绑定本地端口
② 开启监听 ③ 解析请求数据包 ④ 回发响应数据包三、初始化WebServer
文件所在 ESP8266_NONOS_SDK-2.1.0\IoT_Demo\user/user_webserver.c。
创建TCP服务端,参考。 传入参数,本地端口80。void ICACHE_FLASH_ATTRuser_webserver_init(uint32 port){ tcp_websvr_espconn.type = ESPCONN_TCP; tcp_websvr_espconn.state = ESPCONN_NONE; tcp_websvr_espconn.proto.tcp = &esptcp; tcp_websvr_espconn.proto.tcp->local_port = port; // 设置本地端口 espconn_regist_connectcb(&tcp_websvr_espconn, webserver_listen); // 注册监听成功回调函数 espconn_accept(&tcp_websvr_espconn); // 创建 TCP server,建立监听}
四、开启监听
4.1 监听成功回调函数
LOCAL void ICACHE_FLASH_ATTRwebserver_listen(void *arg){ struct espconn *pesp_conn = arg; espconn_regist_recvcb(pesp_conn, webserver_recv); // 注册接收回调函数 espconn_regist_reconcb(pesp_conn, webserver_recon); // 注册发送回调函数 espconn_regist_disconcb(pesp_conn, webserver_discon); // 注册断连回调函数}
4.2 接收回调函数
里面POST请求中解析JSON数据参考
/****************************************************************************** * FunctionName : webserver_recv * Description : Processing the received data from the server * Parameters : arg -- Additional argument to pass to the callback function * pusrdata -- The received data (or NULL when the connection has been closed!) * length -- The length of received data * Returns : none*******************************************************************************/LOCAL void ICACHE_FLASH_ATTRwebserver_recv(void *arg, char *pusrdata, unsigned short length){ URL_Frame *pURL_Frame = NULL; char *pParseBuffer = NULL; bool parse_flag = false; // 解析标志 struct espconn *ptrespconn = arg; /* 如果设备没有在升级 */ if (upgrade_lock == 0) { os_printf("len:%u\n",length); /* 校验数据是否缺失,如果失败跳转做处理 */ if (check_data(pusrdata, length) == false) // ------------------下述 5.1 { os_printf("goto\n"); goto _temp_exit; } /* 校验数据长度后存储起来,开启解析标志 */ parse_flag = save_data(pusrdata, length); // ------------------下述 5.2 if (parse_flag == false) { response_send(ptrespconn, false); // ------------------下述 6.1 } os_printf("pusrdata:%s\n", pusrdata); /* 为URL框架申请空间 */ pURL_Frame = (URL_Frame *)os_zalloc(sizeof(URL_Frame)); // ------------------下述 5.3 /* 解析数据,加入URL框架 */ parse_url(precvbuffer, pURL_Frame); // ------------------下述 5.4 os_printf("Type:%d, Select:%s, Command:%s, Filename:%s ", pURL_Frame->Type,pURL_Frame->pSelect,pURL_Frame->pCommand,pURL_Frame->pFilename); switch (pURL_Frame->Type) { case GET: os_printf("We have a GET request.\n"); if (os_strcmp(pURL_Frame->pSelect, "client") == 0 && os_strcmp(pURL_Frame->pCommand, "command") == 0) { if (os_strcmp(pURL_Frame->pFilename, "info") == 0) { json_send(ptrespconn, INFOMATION); } if (os_strcmp(pURL_Frame->pFilename, "status") == 0) { json_send(ptrespconn, CONNECT_STATUS); } else if (os_strcmp(pURL_Frame->pFilename, "scan") == 0) { char *strstr = NULL; strstr = (char *)os_strstr(pusrdata, "&"); if (strstr == NULL) { if (pscaninfo == NULL) { pscaninfo = (scaninfo *)os_zalloc(sizeof(scaninfo)); } pscaninfo->pespconn = ptrespconn; pscaninfo->pagenum = 0; pscaninfo->page_sn = 0; pscaninfo->data_cnt = 0; wifi_station_scan(NULL, json_scan_cb); } else { strstr ++; if (os_strncmp(strstr, "page", 4) == 0) { if (pscaninfo != NULL) { pscaninfo->pagenum = *(strstr + 5); pscaninfo->pagenum -= 0x30; if (pscaninfo->pagenum > pscaninfo->totalpage || pscaninfo->pagenum == 0) { response_send(ptrespconn, false); } else { json_send(ptrespconn, SCAN); } } else { response_send(ptrespconn, false); } } else if(os_strncmp(strstr, "finish", 6) == 0){ bss_temp = bss_head; while(bss_temp != NULL) { bss_head = bss_temp->next.stqe_next; os_free(bss_temp); bss_temp = bss_head; } bss_head = NULL; bss_temp = NULL; response_send(ptrespconn, true); } else { response_send(ptrespconn, false); } } } else { response_send(ptrespconn, false); } } else if (os_strcmp(pURL_Frame->pSelect, "config") == 0 && os_strcmp(pURL_Frame->pCommand, "command") == 0) { if (os_strcmp(pURL_Frame->pFilename, "wifi") == 0) { ap_conf = (struct softap_config *)os_zalloc(sizeof(struct softap_config)); sta_conf = (struct station_config *)os_zalloc(sizeof(struct station_config)); json_send(ptrespconn, WIFI); os_free(sta_conf); os_free(ap_conf); sta_conf = NULL; ap_conf = NULL; }#if PLUG_DEVICE else if (os_strcmp(pURL_Frame->pFilename, "switch") == 0) { json_send(ptrespconn, SWITCH_STATUS); }#endif else if (os_strcmp(pURL_Frame->pFilename, "reboot") == 0) { json_send(ptrespconn, REBOOT); } else { response_send(ptrespconn, false); } } else if (os_strcmp(pURL_Frame->pSelect, "upgrade") == 0 && os_strcmp(pURL_Frame->pCommand, "command") == 0) { if (os_strcmp(pURL_Frame->pFilename, "getuser") == 0) { json_send(ptrespconn , USER_BIN); } } else { response_send(ptrespconn, false); } break; case POST: os_printf("We have a POST request.\n"); pParseBuffer = (char *)os_strstr(precvbuffer, "\r\n\r\n"); if (pParseBuffer == NULL) { break; } pParseBuffer += 4; if (os_strcmp(pURL_Frame->pSelect, "config") == 0 && os_strcmp(pURL_Frame->pCommand, "command") == 0) { if (os_strcmp(pURL_Frame->pFilename, "reboot") == 0) { if (pParseBuffer != NULL) { if (restart_10ms != NULL) { os_timer_disarm(restart_10ms); } if (rstparm == NULL) { rstparm = (rst_parm *)os_zalloc(sizeof(rst_parm)); } rstparm->pespconn = ptrespconn; rstparm->parmtype = REBOOT; if (restart_10ms == NULL) { restart_10ms = (os_timer_t *)os_malloc(sizeof(os_timer_t)); } os_timer_setfn(restart_10ms, (os_timer_func_t *)restart_10ms_cb, NULL); os_timer_arm(restart_10ms, 10, 0); // delay 10ms, then do response_send(ptrespconn, true); } else { response_send(ptrespconn, false); } } else if (os_strcmp(pURL_Frame->pFilename, "wifi") == 0) { if (pParseBuffer != NULL) { struct jsontree_context js; user_esp_platform_set_connect_status(DEVICE_CONNECTING); if (restart_10ms != NULL) { os_timer_disarm(restart_10ms); } if (ap_conf == NULL) { ap_conf = (struct softap_config *)os_zalloc(sizeof(struct softap_config)); } if (sta_conf == NULL) { sta_conf = (struct station_config *)os_zalloc(sizeof(struct station_config)); } jsontree_setup(&js, (struct jsontree_value *)&wifi_req_tree, json_putchar); json_parse(&js, pParseBuffer); if (rstparm == NULL) { rstparm = (rst_parm *)os_zalloc(sizeof(rst_parm)); } rstparm->pespconn = ptrespconn; rstparm->parmtype = WIFI; if (sta_conf->ssid[0] != 0x00 || ap_conf->ssid[0] != 0x00) { ap_conf->ssid_hidden = 0; ap_conf->max_connection = 4; if (restart_10ms == NULL) { restart_10ms = (os_timer_t *)os_malloc(sizeof(os_timer_t)); } os_timer_disarm(restart_10ms); os_timer_setfn(restart_10ms, (os_timer_func_t *)restart_10ms_cb, NULL); os_timer_arm(restart_10ms, 10, 0); // delay 10ms, then do } else { os_free(ap_conf); os_free(sta_conf); os_free(rstparm); sta_conf = NULL; ap_conf = NULL; rstparm =NULL; } response_send(ptrespconn, true); } else { response_send(ptrespconn, false); } } else if (os_strcmp(pURL_Frame->pFilename, "switch") == 0) { if (pParseBuffer != NULL) { struct jsontree_context js; jsontree_setup(&js, (struct jsontree_value *)&StatusTree, json_putchar); json_parse(&js, pParseBuffer); // ----------Altered by Leung 2018.11.12-------START if (user_plug_get_status() == 1) // 获取继电器状态 { data_send(ptrespconn, true, "{\"status\": 1}"); // 返回APP继电器状态——打开 } else { data_send(ptrespconn, true, "{\"status\": 0}"); // 返回APP继电器状态——关闭 } // ----------Altered by Leung 2018.11.12-------END } else { response_send(ptrespconn, false); } } else { response_send(ptrespconn, false); } } else if(os_strcmp(pURL_Frame->pSelect, "upgrade") == 0 && os_strcmp(pURL_Frame->pCommand, "command") == 0){ if (os_strcmp(pURL_Frame->pFilename, "start") == 0){ response_send(ptrespconn, true); os_printf("local upgrade start\n"); upgrade_lock = 1; system_upgrade_init(); system_upgrade_flag_set(UPGRADE_FLAG_START); os_timer_disarm(&upgrade_check_timer); os_timer_setfn(&upgrade_check_timer, (os_timer_func_t *)upgrade_check_func, NULL); os_timer_arm(&upgrade_check_timer, 120000, 0); } else if (os_strcmp(pURL_Frame->pFilename, "reset") == 0) { response_send(ptrespconn, true); os_printf("local upgrade restart\n"); system_upgrade_reboot(); } else { response_send(ptrespconn, false); } }else { response_send(ptrespconn, false); } break; } if (precvbuffer != NULL){ os_free(precvbuffer); precvbuffer = NULL; } os_free(pURL_Frame); pURL_Frame = NULL; _temp_exit: ; } else if(upgrade_lock == 1){ local_upgrade_download(ptrespconn,pusrdata, length); if (precvbuffer != NULL){ os_free(precvbuffer); precvbuffer = NULL; } os_free(pURL_Frame); pURL_Frame = NULL; }}
五、解析请求数据包
5.1 check_data()
校验收到数据是否正确,判断收到数据的总长度是否等于“Content-Length:”里所描述的长度。
LOCAL bool ICACHE_FLASH_ATTRcheck_data(char *precv, uint16 length){ char length_buf[10] = { 0}; char *ptemp = NULL; char *pdata = NULL; char *tmp_precvbuffer; uint16 tmp_length = length; uint32 tmp_totallength = 0; ptemp = (char *)os_strstr(precv, "\r\n\r\n"); if (ptemp != NULL) { tmp_length -= ptemp - precv; tmp_length -= 4; tmp_totallength += tmp_length; pdata = (char *)os_strstr(precv, "Content-Length: "); if (pdata != NULL){ pdata += 16; tmp_precvbuffer = (char *)os_strstr(pdata, "\r\n"); if (tmp_precvbuffer != NULL){ os_memcpy(length_buf, pdata, tmp_precvbuffer - pdata); dat_sumlength = atoi(length_buf); os_printf("A_dat:%u,tot:%u,lenght:%u\n",dat_sumlength,tmp_totallength,tmp_length); if(dat_sumlength != tmp_totallength){ return false; } } } } return true;}
5.2 save_data()
将收到的数据校验长度后,存储起来。
LOCAL char *precvbuffer;static uint32 dat_sumlength = 0;LOCAL bool ICACHE_FLASH_ATTRsave_data(char *precv, uint16 length){ bool flag = false; char length_buf[10] = { 0}; char *ptemp = NULL; char *pdata = NULL; uint16 headlength = 0; static uint32 totallength = 0; ptemp = (char *)os_strstr(precv, "\r\n\r\n"); if (ptemp != NULL) { length -= ptemp - precv; length -= 4; totallength += length; headlength = ptemp - precv + 4; pdata = (char *)os_strstr(precv, "Content-Length: "); if (pdata != NULL) { pdata += 16; precvbuffer = (char *)os_strstr(pdata, "\r\n"); if (precvbuffer != NULL) { os_memcpy(length_buf, pdata, precvbuffer - pdata); dat_sumlength = atoi(length_buf); } } else { if (totallength != 0x00){ totallength = 0; dat_sumlength = 0; return false; } } if ((dat_sumlength + headlength) >= 1024) { precvbuffer = (char *)os_zalloc(headlength + 1); os_memcpy(precvbuffer, precv, headlength + 1); } else { precvbuffer = (char *)os_zalloc(dat_sumlength + headlength + 1); os_memcpy(precvbuffer, precv, os_strlen(precv)); } } else { if (precvbuffer != NULL) { totallength += length; os_memcpy(precvbuffer + os_strlen(precvbuffer), precv, length); } else { totallength = 0; dat_sumlength = 0; return false; } } if (totallength == dat_sumlength) { totallength = 0; dat_sumlength = 0; return true; } else { return false; }}
5.3 URL_Frame
typedef struct URL_Frame { enum ProtocolType Type; char pSelect[URLSize]; char pCommand[URLSize]; char pFilename[URLSize];} URL_Frame;
5.4 parse_url()
/****************************************************************************** * FunctionName : parse_url * Description : parse the received data from the server * Parameters : precv -- the received data * purl_frame -- the result of parsing the url * Returns : none*******************************************************************************/LOCAL void ICACHE_FLASH_ATTRparse_url(char *precv, URL_Frame *purl_frame){ char *str = NULL; uint8 length = 0; char *pbuffer = NULL; char *pbufer = NULL; if (purl_frame == NULL || precv == NULL) { return; } pbuffer = (char *)os_strstr(precv, "Host:"); if (pbuffer != NULL) { length = pbuffer - precv; pbufer = (char *)os_zalloc(length + 1); pbuffer = pbufer; os_memcpy(pbuffer, precv, length); os_memset(purl_frame->pSelect, 0, URLSize); os_memset(purl_frame->pCommand, 0, URLSize); os_memset(purl_frame->pFilename, 0, URLSize); if (os_strncmp(pbuffer, "GET ", 4) == 0) { purl_frame->Type = GET; pbuffer += 4; } else if (os_strncmp(pbuffer, "POST ", 5) == 0) { purl_frame->Type = POST; pbuffer += 5; } pbuffer ++; str = (char *)os_strstr(pbuffer, "?"); if (str != NULL) { length = str - pbuffer; os_memcpy(purl_frame->pSelect, pbuffer, length); str ++; pbuffer = (char *)os_strstr(str, "="); if (pbuffer != NULL) { length = pbuffer - str; os_memcpy(purl_frame->pCommand, str, length); pbuffer ++; str = (char *)os_strstr(pbuffer, "&"); if (str != NULL) { length = str - pbuffer; os_memcpy(purl_frame->pFilename, pbuffer, length); } else { str = (char *)os_strstr(pbuffer, " HTTP"); if (str != NULL) { length = str - pbuffer; os_memcpy(purl_frame->pFilename, pbuffer, length); } } } } os_free(pbufer); } else { return; }}
六、回发响应数据包
6.1 response_send()
回复简单格式,里面调用data_send()
/****************************************************************************** * FunctionName : response_send * Description : processing the send result * Parameters : arg -- argument to set for client or server * responseOK -- true or false * Returns : none*******************************************************************************/LOCAL void ICACHE_FLASH_ATTRresponse_send(void *arg, bool responseOK){ struct espconn *ptrespconn = arg; data_send(ptrespconn, responseOK, NULL);}
6.2 json_send()
里面生成JSON数据json_ws_send()参考
/****************************************************************************** * FunctionName : json_send * Description : processing the data as json format and send to the client or server * Parameters : arg -- argument to set for client or server * ParmType -- json format type * Returns : none*******************************************************************************/LOCAL void ICACHE_FLASH_ATTRjson_send(void *arg, ParmType ParmType){ char *pbuf = NULL; pbuf = (char *)os_zalloc(jsonSize); struct espconn *ptrespconn = arg; switch (ParmType) { #if PLUG_DEVICE case SWITCH_STATUS: json_ws_send((struct jsontree_value *)&StatusTree, "switch", pbuf); os_printf("switch:%s", pbuf); break;#endif case INFOMATION: json_ws_send((struct jsontree_value *)&INFOTree, "info", pbuf); break; case WIFI: json_ws_send((struct jsontree_value *)&wifi_info_tree, "wifi", pbuf); break; case CONNECT_STATUS: json_ws_send((struct jsontree_value *)&con_status_tree, "info", pbuf); break; case USER_BIN: json_ws_send((struct jsontree_value *)&userinfo_tree, "user_info", pbuf); break; case SCAN: { u8 i = 0; u8 scancount = 0; struct bss_info *bss = NULL; bss = bss_head; if (bss == NULL) { os_free(pscaninfo); pscaninfo = NULL; os_sprintf(pbuf, "{\n\"successful\": false,\n\"data\": null\n}"); } else { do { if (pscaninfo->page_sn == pscaninfo->pagenum) { pscaninfo->page_sn = 0; os_sprintf(pbuf, "{\n\"successful\": false,\n\"meessage\": \"repeated page\"\n}"); break; } scancount = scannum - (pscaninfo->pagenum - 1) * 8; if (scancount >= 8) { pscaninfo->data_cnt += 8; pscaninfo->page_sn = pscaninfo->pagenum; if (pscaninfo->data_cnt > scannum) { pscaninfo->data_cnt -= 8; os_sprintf(pbuf, "{\n\"successful\": false,\n\"meessage\": \"error page\"\n}"); break; } json_ws_send((struct jsontree_value *)&scan_tree, "scan", pbuf); } else { pscaninfo->data_cnt += scancount; pscaninfo->page_sn = pscaninfo->pagenum; if (pscaninfo->data_cnt > scannum) { pscaninfo->data_cnt -= scancount; os_sprintf(pbuf, "{\n\"successful\": false,\n\"meessage\": \"error page\"\n}"); break; } char *ptrscanbuf = (char *)os_zalloc(jsonSize); char *pscanbuf = ptrscanbuf; os_sprintf(pscanbuf, ",\n\"ScanResult\": [\n"); pscanbuf += os_strlen(pscanbuf); for (i = 0; i < scancount; i ++) { JSONTREE_OBJECT(page_tree, JSONTREE_PAIR("page", &scaninfo_tree)); json_ws_send((struct jsontree_value *)&page_tree, "page", pscanbuf); os_sprintf(pscanbuf + os_strlen(pscanbuf), ",\n"); pscanbuf += os_strlen(pscanbuf); } os_sprintf(pscanbuf - 2, "]\n"); JSONTREE_OBJECT(scantree, JSONTREE_PAIR("TotalPage", &scan_callback), JSONTREE_PAIR("PageNum", &scan_callback)); JSONTREE_OBJECT(scanres_tree, JSONTREE_PAIR("Response", &scantree)); JSONTREE_OBJECT(scan_tree, JSONTREE_PAIR("scan", &scanres_tree)); json_ws_send((struct jsontree_value *)&scan_tree, "scan", pbuf); os_memcpy(pbuf + os_strlen(pbuf) - 4, ptrscanbuf, os_strlen(ptrscanbuf)); os_sprintf(pbuf + os_strlen(pbuf), "}\n}"); os_free(ptrscanbuf); } } while (0); } break; } default : break; } data_send(ptrespconn, true, pbuf); os_free(pbuf); pbuf = NULL;}
6.3 data_send()
/****************************************************************************** * FunctionName : data_send * Description : processing the data as http format and send to the client or server * Parameters : arg -- argument to set for client or server * responseOK -- true or false * psend -- The send data * Returns :*******************************************************************************/LOCAL void ICACHE_FLASH_ATTRdata_send(void *arg, bool responseOK, char *psend){ uint16 length = 0; char *pbuf = NULL; char httphead[256]; struct espconn *ptrespconn = arg; os_memset(httphead, 0, 256); if (responseOK) { os_sprintf(httphead, "HTTP/1.0 200 OK\r\nContent-Length: %d\r\nServer: lwIP/1.4.0\r\n", psend ? os_strlen(psend) : 0); if (psend) { os_sprintf(httphead + os_strlen(httphead), "Content-type: application/json\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache\r\n\r\n"); length = os_strlen(httphead) + os_strlen(psend); pbuf = (char *)os_zalloc(length + 1); os_memcpy(pbuf, httphead, os_strlen(httphead)); os_memcpy(pbuf + os_strlen(httphead), psend, os_strlen(psend)); } else { os_sprintf(httphead + os_strlen(httphead), "\n"); length = os_strlen(httphead); } } else { os_sprintf(httphead, "HTTP/1.0 400 BadRequest\r\n\Content-Length: 0\r\nServer: lwIP/1.4.0\r\n\n"); length = os_strlen(httphead); } if (psend) { espconn_sent(ptrespconn, pbuf, length); } else { espconn_sent(ptrespconn, httphead, length); } if (pbuf) { os_free(pbuf); pbuf = NULL; }}
七、printf打印内容
len:491A_dat:25,tot:25,lenght:25pusrdata:POST /config?command=switch HTTP/1.1Accept: application/json,application/xml,application/xhtml+xml,text/html;q=0.9,image/webp,*/*;q=0.8Accept-Encoding: gzip, deflateAccept-Language: zh-CN,zhConnection: keep-aliveContent-Type: application/json; charset=utf-8User-Agent: Mozilla/5.0 (Linux; U; Android 5.0.2; zh-cn; Redmi Note 2 Build/LRX22G) AppleWebKit/533.1 (KHTML, like Gecko) Version/5.0 Mobile Safari/533.1Content-Length: 25Host: 192.168.0.52{"Response":{"status":0}}Type:1, Select:config, Command:command, Filename:switch We have a POST request.
• 由 写于 2019 年 2 月 27 日
• 参考:[vqhl]
[7qq6]发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2025年03月10日 06时13分03秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!