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請求。