esp8266 下載檔案後如何去除HTTP頭
參考文章:https://www.cnblogs.com/arnoldlu/p/6497837.html
通過HTTP 發出GET請求從伺服器下載檔案,特別是Bin檔案等二進位制檔案。在TCP接收回調函式中,利用os_printf("%s",pdata)試圖從串列埠輸出GET的內容,但由於在二進位制檔案中可能存在\0,而導致輸出中止,os_printf() 用於輸出字串,而字串遇到'\0'結束。因此採用os_printf()以不合適,採用uart0_tx_buffer 輸出指定資料快取中指定長度的資料。
對於本文的主題,參考文章提到的http-parser和fast-http是解析HTTP協議兩個不錯的版本。本文以http-parser為研究物件。
對於傳送GET請求request和解析 Response HTTP頭,瞭解請求和響應時HTTP的組成是第一步。
HTTP請求和響應格式
Request格式——請求頭(向伺服器發出請求用)
<Request-line> METHOD /path-to-resource HTTP/Version-number
<headers> Header-Name-1:value
Header-Name-2:value
<blank line> 空行
[<request-body>] 可選的其他任意資料
說明:第一行必須是一個請求行(request-line),用來說明請求型別,要訪問的資源以及所用到的HTTP版本;
第一行中Method 是請求方法-常用的有GET、POST。對於HTTP1.1目前支援7鍾請求方法:GET/POST/HEAD/OPTION/PUT/DELETE/TRACE。他們的不同之處,點選此處
緊接的是一個header 小節, 用來說明伺服器要使用的附加資訊
之後,必須是一個空行
最後,是可選的其他任意資料---稱為主體(body)
例子:
GET/sample.jsp HTTP/1.1 第一部分說明這是一個GET請求, 第二步峰說明資源在根目錄的/sample.jsp ,版本為HTTP1.1
Accept:image/gif.image/jpeg.*/* 請求的第一個首部Header, 瀏覽器支援的EMIME型別分別是。。。
Accept-Language:zh-cn 瀏覽器支援的語言分別是:簡體中文
Connection:Keep-Alive 該Header通常設定為Keep-Alive 表示客戶端和伺服器端是持久連線
Host:www.google.cn Host 這一Header指出請求的目的地是:www.google.cn
User-Agent:Mozila/4.0(compatible:MSIE5.01:Windows NT5.0)
Accept-Encoding:gzip,deflate. 瀏覽器支援的壓縮編碼是給IP和deflate
User-Agent這一header指出伺服器端和客戶端指令碼均訪問她,她是瀏覽器型別檢測邏輯的重要基礎,該資訊由你的瀏覽器來定義,並且在每個請求中主動傳送; 這是使用的額代理是Mozilla/4.0(域名)
第三部分是空行,即使不存在請求主題,這個空行也是必需的。
這個空行,非常重要,她表示請求頭已經結束,接下去的是請求正文(body)。請求正文中可以包含客戶提交的查詢字串資訊。
例如username=fangle&password=1234
上面是我們發出請求的基礎,在ESP8266中發出請求一定不能出錯,這一步一出錯,後面就OVER了。下面進行Respost頭的分析,這是我們解析Recv的應答頭的基礎。
HTTP 響應格式
<status-line> Http/version-number status code message
<headers> header-Name-1:value
<blank line> 也有
[<respones-body> ] Optional Response body
第一行是狀態行,由HTTP協議版本號,狀態碼、狀態訊息三部分組成,各部分以空格隔開。
HTTP/version-number表示HTTP協議的版本號, status-code 是數字形式的狀態碼 mesage是相應的狀態描述。
HTTP/1.1中定義了5類狀態碼,狀態碼由三位數字組成,第一個數字表示響應的類別。
1XX 提示資訊 --表示請求已被成功接收,繼續處理
2XX 成功-表示請求已被成功接收,理解,接收
3XX 重定向- 請求有語法錯誤或請求無法實現
4XX 客戶端錯誤 - 請求有語法錯誤或請求無法實現
5XX 伺服器端錯誤 - 伺服器未能實現合法的請求
200 OK:最常見的就是成功響應狀態碼200了, 這表明該請求被成功地完成,所請求的資源傳送回客戶端。
404 Not Found :請求資源不存在,例如輸出URL錯誤。
對於重定向而言,就是新的URL會在response 中的Location中返回,瀏覽器將會自動使用新的URL發出新的Request。
第二行開始是Response的Header部分
其中對於我們解析下載的檔案解析有用的Header為Content-Length: Content-Length 實體包頭域用來指明正文body的長度,以位元組方式儲存的十進位制數字來表示,也就是一個數字元號佔一個位元組,用於對應的ASCII碼儲存傳輸。
Content-Type實體報頭域用來指明發送給接收者的實體正文的媒體型別。如Content-Type:text/html;charset=ISO-8859-1
缺圖
其他Header部分以後補充
在Header與正文(Body)之間也有一個空行,這也是必需的。
這樣根據這一特點,有人也用在其中找到"\r\n\r\n"來正文部分。這也是可行一個法子。
接下去,我們就來探究一下http-parser來解析Http的內容。 HTTP-Parser 是用C語言寫的HTTP資訊解析器。它可用於解析請求和響應。該解析器設計增強HTTP應用的效能。
再接下去,講解下HTTP-Praser的使用。在每個TCP連線中使用一個http_parser物件,用http_parser_init()初始化她以及設定回撥。
當socket(套接字)接收到Response時,執行解析器並檢查錯誤。
http_parser_execute(parser, &settings, buf, recved); 執行解析過程,其原型
/**********
*執行解析器,返回解析的位元組,Sets parse->http_errno
*input parse指向解析器物件; settings指向 回撥函式結構體設定,data指向待解析的資料, len:帶解析的長度
*****************/
size_t http_parser_execute(http_parser *parser,
const http_parser_settings *settings,const char *data,
size_t len);
對於結構體http_parser_settings
struct http_parser_settings {
http_cb on_message_begin;
http_data_cb on_url;
http_data_cb on_status;
http_data_cb on_header_field;
http_data_cb on_header_value;
http_cb on_headers_complete;
http_data_cb on_body;
http_cb on_message_complete;
/* When on_chunk_header is called, the current chunk length is stored
* in parser->content_length.
*/
http_cb on_chunk_header;
http_cb on_chunk_complete;
}; http_cb 和 http_data_cb都是函式指標型別,如下所示
typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
typedef int (*http_cb) (http_parser*);
程式碼一般如下所示:
int on_header_field(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header field: %.*s\n", (int)length, at);
return 0;
}
int on_header_value(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Header value: %.*s\n", (int)length, at);
return 0;
}
int on_body(http_parser* _, const char* at, size_t length) {
(void)_;
printf("Body: %.*s\n", (int)length, at); //at就是所有的正文資料,以及lenght是其長度
return 0;
}
static http_parser_settings settings_null = //http_parser的回撥函式,需要獲取HEADER或者BODY資訊,可以在這裡面處理。
{.on_message_begin = 0
,.on_header_field = on_header_field
,.on_header_value = on_header_value
,.on_url = 0
,.on_status = 0
,.on_body = on_body
,.on_headers_complete = 0
,.on_message_complete = 0
};
在ESP8266 的接收回調函式中:
#include "http_parser.h"
static http_parser 8parser;
void espconn_recv_cb( void *arg, char *pdata, unsigned short len)
{
struct espconn *pesp_conn = arg;
parase_recv(pdata, len);
}
void parase_recv(const char *buf, unsigned short len)
{
int i;
// float start, end;
size_t parsed; //解析的位元組數
parser = (struct http_parser *)os_zalloc(http_parser);
http_parser_init(parse,HTTP_RESPONSE); //初始化Parser 為Response型別
parsed = http_parser_execute(parser, &settings_null, buf, len); 執行解析過程
free(parser)
}
最後我們在講講那種偷懶的方法——找"\r\n\r\n"。
#include <stdlib>
int length=0;
char *binContent= NULL;
void parase_recv(const char *buf, unsigned short len)
{
char *PA;
char *PB;
char *PC;
char strLength[32]={0};
if(!(*buf)) return;
PA = buf;
PB = os_strstr(PA,"Content-Length:")
PC = os_strstr(PB,"\r\n");
memcpy(strLength,PB, PC-PB);
length = atoi(strLength);
binContent = (char *)zalloc(length );
PC = NULL;
if(PC= os_strstr(PB,"\r\n\r\n"))
PC +=os_strlen("\r\n\r\n");
memcpy(binConten, PC, length);
}
對於http-paraser的程式碼解析,具體再下一章具體解釋。