1. 程式人生 > >HTTP程式設計—完結

HTTP程式設計—完結

4.4.4客戶端和伺服器端互動

HTTP1.0定義了3種請求方法:GET、POST、HEAD;HTTP1.1新增5種請求方法:PUT、DELETE、CONNECT、OPTIONS、TRACE。

GET:請求指定的頁面資訊,並返回實體主體。

POST:向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案)。資料被包含在請求體中。POST請求可能會導致新的資源的建立和/或已有資源的修改。

HEAD:類似於get請求,只不過返回的響應中沒有具體的內容,用於獲取報頭。

PUT:從客戶端向伺服器傳送的資料取代指定的文件的內容。

DELETE:請求伺服器刪除指定的頁面。

CONNECT:HTTP/1.1協議中預留給能夠將連線改為管道方式的代理伺服器。

OPTIONS:允許客戶端檢視伺服器的效能。

TRACE:回顯伺服器收到的請求,主要用於測試或診斷。

HTTP響應(Reponce)的狀態碼:

1xx:指示資訊--表示請求已接收,繼續處理。

2xx:成功—表示請求已被成功接收、理解、接受。

3xx:重定向—要完成請求必須進行更進一步的操作。

4xx:客戶端錯誤—請求有語法錯誤或請求無法實現。

5xx:伺服器端錯誤—伺服器未能實現合法的請求。

常見狀態碼:

200 OK //客戶端請求成功

400 Bad Request //客戶端請求有語法錯誤,不能被伺服器所理解

401 Unauthorized  //請求未經授權,這個狀態程式碼必須和WWW-Authenticate報頭域一起使用

403 Forbidden   //伺服器收到請求,但是拒絕提供服務

404 Not Found      //請求資源不存在,eg:輸入了錯誤的URL

500 Internal Server Error //伺服器發生不可預期的錯誤

503 Server Unavailable //伺服器當前不能處理客戶端的請求,一段時間後可能恢復正常

使用WireShark或fiddler抓HTTP協議包,如下所示:

 

上圖紅色部分是HTTP客戶端請求(GET),藍色部分是HTTP伺服器端響應(200 ok表示請求成功)。

常用的請求有GET和POST,區別如下:

GET請求:

GET /books/?name=Professional%20Ajax HTTP/1.1
Host: www.wrox.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)
Gecko/20050225 Firefox/1.0.1
Connection: Keep-Alive
POST請求:
POST / HTTP/1.1 //請求行

Host: www.wrox.com //2-7行是請求頭部

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.6)

Gecko/20050225 Firefox/1.0.1

Content-Type: application/x-www-form-urlencoded

Content-Length: 40

Connection: Keep-Alive
(----此處空一行----) //空行

name=Professional%20Ajax&publisher=Wiley //請求資料,即HTTP訊息主體

這是我從網上找的2個例子。當我們傳送一些資料請求時,如使用者名稱、密碼,GET請求是放入URL中傳送的;而且GET請求是可被快取的,引數保留在瀏覽器歷史中。因此會被爬蟲爬走一些關鍵性的資料。

若使用POST請求,資料是存放在HTTP訊息主體中傳送的;且POST請求不會被快取,引數不會儲存在瀏覽器中。因此使用POST請求更加安全。

參考部落格:https://www.cnblogs.com/ranyonsue/p/5984001.html

4.4.5 CURL程式設計

在win10平臺下,使用vs2017編譯CURL。

(1)在https://curl.haxx.se/download.html下載原始碼。

(2)解壓後,放入D盤,進入D:\curl-7.62.0\winbuild,並複製其資料夾路徑。

(3)開啟開始選單,找到vs2017,若編譯64位,則選擇”適用於VS 2017的x64本機工具命令提示”;若編譯32位,則選擇”適用於VS 2017的x86本機工具命令提示”。在這裡,我們選擇編譯64位。

(4)進入命令列後,輸入cd D:\curl-7.62.0\winbuild,進入winbuild目錄;然後輸入命令nmake /f Makefile.vc mode=static VC=15 MACHINE=x64 ENABLE_IDN=no DEBUG=no,等待即可。注:這裡一定要加上ENABLE_IDN=no,不然它會按照ENABLE_IDN=yes來編譯,程式碼執行後會出現”無法解析的外部符號 [email protected],該符號在函式 _curl_win32_idn_to_ascii 中被引用”和”無法解析的外部符號 [email protected],該符號在函式 _curl_win32_ascii_to_idn 中被引用”這樣的錯誤。

(5)若使用動態編譯,則將”mode=static”改為”mode=dll”;若使用x86平臺,將“MACHINE=x64”改為“MACHINE=x86”;  若需要debug版,將“DEBUG=no”改為“DEBUG=yes”;若使用其它版本的Visual Studio,則”VC”後的數字需要改為對應Visual Studio的版本。

(6)編譯完成後,開啟curl-7.62.0\builds資料夾,libcurl-vc15-x64-release-static-ipv6-sspi-winssl存放的就是編譯結果(名稱最短的那個資料夾)。我們可以將編譯完成的資料夾放入其他路徑,以便長期使用,如下圖所示。

編譯完成CURL,該如何使用呢?

(1)在vs2017中,新建一個專案。右擊:專案->屬性;將配置改為”所有配置”,即包括Debug除錯版本和Release發行版本;平臺選擇”x64”。

(2)選擇VC++目錄,在包含目錄處,新增”C:\CURL_WIN_X64\include”;在庫目錄處,新增”C:\CURL_WIN_X64\lib”。因為我們將編譯好的標頭檔案、庫檔案放在C盤CURL_WIN_X64目錄下。

(3)選擇目錄:編譯器->輸入,在附加依賴項處,新增”libcurl_a.lib”、”ws2_32.lib”、”Crypt32.lib”、”wldap32.lib”,如下圖所示。

(4)若使用靜態編譯的CURL版本,則需要加入預處理定義:CURL_STATICLIB。在C/C++->前處理器->前處理器定義,新增CURL_STATICLIB。

(5)若使用的是Release版本,則進入目錄:C/C++->程式碼生成,在執行庫處,選擇MD。官方不建議使用MT和MTd,即靜態連結多執行緒庫,加d表示是Debug版本。

(6)在解決方案那裡,解決方案配置選擇Release或Debug,解決方案平臺選擇x64。

//http傳送GET請求

#include <curl/curl.h>    

 

static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream)

{

size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);

return written;

}

 

void curl_get_req(const char * url,const char * filename)

{

CURL *curl_handle;

FILE *pagefile;

//static const char *pagefilename = "page.out";

 

if (url==NULL || filename==NULL)

{

return;

}

 

curl_global_init(CURL_GLOBAL_ALL);

 

/* init the curl session */

curl_handle = curl_easy_init();

 

/* set URL to get here */

curl_easy_setopt(curl_handle, CURLOPT_URL,url);

 

/* Switch on full protocol/debug output while testing */

curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);

 

/* disable progress meter, set to 0L to enable and disable debug output */

curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);

 

/* send all data to this function  */

curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_data);

 

/* open the file */

//pagefile = fopen(pagefilename, "wb");

errno_t err = fopen_s(&pagefile,filename,"wb");

if (err==0) {

//if (pagefile) {

/* write the page body to this file handle */

curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);

 

/* get it! */

curl_easy_perform(curl_handle);

 

/* close the header file */

fclose(pagefile);

}

 

/* cleanup curl stuff */

curl_easy_cleanup(curl_handle);

 

curl_global_cleanup();

}

 

int main(int argc, char ** argv)

{

static const char * load_png = "baidu.PNG";

const char * url = "https://www.baidu.com/img/bd_logo1.png?where=super";

curl_get_req(url, load_png);

 

return 0;

}

執行結果:

*   Trying 112.80.248.75...

* TCP_NODELAY set

* Connected to www.baidu.com (112.80.248.75) port 443 (#0)

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 1/3)

* schannel: checking server certificate revocation

* schannel: sending initial handshake data: sending 178 bytes...

* schannel: sent initial handshake data: sent 178 bytes

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 2/3)

* schannel: failed to receive handshake, need more data

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 2/3)

* schannel: encrypted data got 3559

* schannel: encrypted data buffer: offset 3559 length 4096

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 2/3)

* schannel: encrypted data got 347

* schannel: encrypted data buffer: offset 347 length 4096

* schannel: sending next handshake data: sending 126 bytes...

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 2/3)

* schannel: encrypted data got 226

* schannel: encrypted data buffer: offset 226 length 4096

* schannel: SSL/TLS handshake complete

* schannel: SSL/TLS connection with www.baidu.com port 443 (step 3/3)

* schannel: stored credential handle in session cache

> GET /img/bd_logo1.png?where=super HTTP/1.1

Host: www.baidu.com

Accept: */*

 

* schannel: client wants to read 16384 bytes

* schannel: encdata_buffer resized 17408

* schannel: encrypted data buffer: offset 0 length 17408

* schannel: encrypted data got 7074

* schannel: encrypted data buffer: offset 7074 length 17408

* schannel: decrypted data length: 4000

* schannel: decrypted data added: 4000

* schannel: decrypted data cached: offset 4000 length 16384

* schannel: encrypted data length: 3045

* schannel: encrypted data cached: offset 3045 length 17408

* schannel: decrypted data length: 96

* schannel: decrypted data added: 96

* schannel: decrypted data cached: offset 4096 length 16384

* schannel: encrypted data length: 2920

* schannel: encrypted data cached: offset 2920 length 17408

* schannel: failed to decrypt data, need more data

* schannel: schannel_recv cleanup

* schannel: decrypted data returned 4096

* schannel: decrypted data buffer: offset 0 length 16384

< HTTP/1.1 200 OK

< Accept-Ranges: bytes

< Cache-Control: max-age=315360000

< Connection: Keep-Alive

< Content-Length: 7877

< Content-Type: image/png

< Date: Mon, 12 Nov 2018 13:53:40 GMT

< Etag: "1ec5-502264e2ae4c0"

< Expires: Thu, 09 Nov 2028 13:53:40 GMT

< Last-Modified: Wed, 03 Sep 2014 10:00:27 GMT

< P3p: CP=" OTI DSP COR IVA OUR IND COM "

< Server: Apache

< Set-Cookie: BAIDUID=DA921C00BF8A31532A27D2D6BFB1F355:FG=1; expires=Tue, 12-Nov-19 13:53:40 GMT; max-age=31536000; path=/; domain=.baidu.com; version=1

<

* schannel: client wants to read 4287 bytes

* schannel: encrypted data buffer: offset 2920 length 17408

* schannel: encrypted data got 1425

* schannel: encrypted data buffer: offset 4345 length 17408

* schannel: decrypted data length: 4000

* schannel: decrypted data added: 4000

* schannel: decrypted data cached: offset 4000 length 16384

* schannel: encrypted data length: 316

* schannel: encrypted data cached: offset 316 length 17408

* schannel: decrypted data length: 287

* schannel: decrypted data added: 287

* schannel: decrypted data cached: offset 4287 length 16384

* schannel: encrypted data buffer: offset 0 length 17408

* schannel: decrypted data buffer: offset 4287 length 16384

* schannel: schannel_recv cleanup

* schannel: decrypted data returned 4287

* schannel: decrypted data buffer: offset 0 length 16384

* Connection #0 to host www.baidu.com left intact

圖片如下所示:

POST請求中,有四種常見的提交資料方式:application/x-www-form-urlencoded、multipart/form-data、application/json、text/xml。將application/json作為請求頭,用來告訴伺服器訊息主體是序列化後的JSON字串。大家可以自己用JSON方式來實現POST請求。