1. 程式人生 > >esp8266 下載檔案後如何去除HTTP頭

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的程式碼解析,具體再下一章具體解釋。