C++編程筆記:使用WinHTTP實現HTTP訪問


轉載:https://blog.csdn.net/elaine_bao/article/details/51754882

實現HTTP訪問的流程包括以下幾步:
1, 首先我們打開一個Session獲得一個HINTERNET session句柄;
2, 然后我們使用這個session句柄與服務器連接得到一個HINTERNET connect句柄;
3, 然后我們使用這個connect句柄來打開Http請求得到一個HINTERNET request句柄;
4, 這時我們就可以使用這個request句柄來發送數據與讀取從服務器返回的數據;
5, 最后依次關閉request,connect,session句柄。

微軟提供了兩套http訪問的接口:WinHTTP和WinINet。WinHTTP比WinINet更加安全和健壯,可以認為WinHTTP是WinINet的升級版本。這兩套API包含了很多相似的函數與宏定義,訪問的流程也是完全類似的(上述5步)。本文主要通過WinHTTP實現post請求方法,嚴格按照上述5個步驟給大家進行講解。

  1 #include <iostream>
  2 #include <tchar.h>
  3 #include <string>
  4 #include <windows.h>
  5 #include <winhttp.h>
  6 #pragma comment(lib, "winhttp.lib")
  7 
  8 using namespace std;
  9 
 10 int _tmain(int argc, _TCHAR* argv[])
 11 {
 12     HINTERNET hSession = NULL;
 13     HINTERNET hConnect = NULL;
 14     HINTERNET hRequest = NULL;
 15 
 16     //1. 初始化一個WinHTTP-session句柄,參數1為此句柄的名稱
 17     hSession = WinHttpOpen(L"csdn@elaine_bao", NULL, NULL, NULL, NULL);
 18     if (hSession == NULL) {
 19         cout<<"Error:Open session failed: "<<GetLastError()<<endl;
 20         return -1;
 21     }
 22 
 23     //2. 通過上述句柄連接到服務器,需要指定服務器IP和端口號。若連接成功,返回的hConnect句柄不為NULL
 24     hConnect = WinHttpConnect(hSession, L"192.168.50.112", (INTERNET_PORT)8080, 0);
 25     if (hConnect == NULL) {
 26         cout << "Error:Connect failed: " << GetLastError()<<endl;
 27         return -1;
 28     }
 29 
 30     //3. 通過hConnect句柄創建一個hRequest句柄,用於發送數據與讀取從服務器返回的數據。
 31     hRequest = WinHttpOpenRequest(hConnect, L"Post", L"getServiceInfo", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
 32     //其中參數2表示請求方式,此處為Post;參數3:給定Post的具體地址,如這里的具體地址為http://192.168.50.112/getServiceInfo
 33     if (hRequest == NULL) {
 34         cout << "Error:OpenRequest failed: " << GetLastError() << endl;
 35         return -1;
 36     }
 37 
 38     //4-1. 向服務器發送post數據
 39     //(1) 指定發送的數據內容
 40     string data = "This is my data to be sent"; 
 41     const void *ss = (const char *)data.c_str();
 42 
 43     //(2) 發送請求
 44     BOOL bResults;
 45     bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, const_cast<void*>(ss), data.length(), data.length(), 0);
 46     if (!bResults){
 47         cout << "Error:SendRequest failed: " << GetLastError() << endl;
 48         return -1;
 49     }
 50     else{
 51         //(3) 發送請求成功則准備接受服務器的response。注意:在使用 WinHttpQueryDataAvailable和WinHttpReadData前必須使用WinHttpReceiveResponse才能access服務器返回的數據
 52         bResults = WinHttpReceiveResponse(hRequest, NULL);
 53     }
 54 
 55     //4-2. 獲取服務器返回數據的header信息。這一步我用來獲取返回數據的數據類型。
 56     LPVOID lpHeaderBuffer = NULL;
 57     DWORD dwSize = 0;   
 58     if (bResults)
 59     {
 60         //(1) 獲取header的長度
 61         WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF,
 62             WINHTTP_HEADER_NAME_BY_INDEX, NULL,
 63             &dwSize, WINHTTP_NO_HEADER_INDEX);
 64 
 65         //(2) 根據header的長度為buffer申請內存空間
 66         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
 67         {
 68             lpHeaderBuffer = new WCHAR[dwSize / sizeof(WCHAR)];
 69 
 70             //(3) 使用WinHttpQueryHeaders獲取header信息
 71             bResults = WinHttpQueryHeaders(hRequest,
 72                 WINHTTP_QUERY_RAW_HEADERS_CRLF,
 73                 WINHTTP_HEADER_NAME_BY_INDEX,
 74                 lpHeaderBuffer, &dwSize,
 75                 WINHTTP_NO_HEADER_INDEX);
 76         }
 77     }
 78     printf("Header contents: \n%S", lpHeaderBuffer);
 79 
 80     //解析上述header信息會發現服務器返回數據的charset為uft-8。這意味着后面需要對獲取到的raw data進行寬字符轉換。一開始由於沒有意識到需要進行轉換所以得到的數據都是亂碼。
 81     //出現亂碼的原因是:HTTP在傳輸過程中是二值的,它並沒有text或者是unicode的概念。HTTP使用7bit的ASCII碼作為HTTP headers,但是內容是任意的二值數據,需要根據header中指定的編碼方式來描述它(通常是Content-Type header).
 82     //因此當你接收到原始的HTTP數據時,先將其保存到char[] buffer中,然后利用WinHttpQueryHearders()獲取HTTP頭,得到內容的Content-Type,這樣你就知道數據到底是啥類型的了,是ASCII還是Unicode或者其他。
 83     //一旦你知道了具體的編碼方式,你就可以通過MultiByteToWideChar()將其轉換成合適編碼的字符,存入wchar_t[]中。
 84     //關於亂碼的解決方案請看4-4
 85 
 86     //4-3. 獲取服務器返回數據
 87     LPSTR pszOutBuffer = NULL;
 88     DWORD dwDownloaded = 0;         //實際收取的字符數
 89     wchar_t *pwText = NULL;
 90     if (bResults)
 91     {
 92         do
 93         {
 94             //(1) 獲取返回數據的大小(以字節為單位)
 95             dwSize = 0;
 96             if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){
 97                 cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
 98                 break;
 99             }           
100             if (!dwSize)    break;  //數據大小為0                
101 
102             //(2) 根據返回數據的長度為buffer申請內存空間
103             pszOutBuffer = new char[dwSize + 1];
104             if (!pszOutBuffer){
105                 cout<<"Out of memory."<<endl;
106                 break;
107             }
108             ZeroMemory(pszOutBuffer, dwSize + 1);       //將buffer置0
109 
110             //(3) 通過WinHttpReadData讀取服務器的返回數據
111             if (!WinHttpReadData(hRequest,pszOutBuffer, dwSize, &dwDownloaded)){
112                 cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
113             }
114             if (!dwDownloaded)
115                 break;
116 
117         } while (dwSize > 0);
118 
119         //4-4. 將返回數據轉換成UTF8
120         DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0);    //返回原始ASCII碼的字符數目       
121         pwText = new wchar_t[dwNum];                                                //根據ASCII碼的字符數分配UTF8的空間
122         MultiByteToWideChar(CP_UTF8, 0, pszOutBuffer, -1, pwText, dwNum);           //將ASCII碼轉換成UTF8
123         printf("Received contents: \n%S", pwText);
124     }
125 
126 
127     //5. 依次關閉request,connect,session句柄
128     if (hRequest) WinHttpCloseHandle(hRequest);
129     if (hConnect) WinHttpCloseHandle(hConnect);
130     if (hSession) WinHttpCloseHandle(hSession);
131 
132 
133     return 0;
134 }

 

微軟示例:https://docs.microsoft.com/en-us/windows/win32/api/winhttp/nf-winhttp-winhttpwritedata

 1 PCSTR pszData = "WinHttpWriteData Example";
 2     DWORD dwBytesWritten = 0;
 3     BOOL  bResults = FALSE;
 4     HINTERNET hSession = NULL,
 5               hConnect = NULL,
 6               hRequest = NULL;
 7 
 8     // Use WinHttpOpen to obtain a session handle.
 9     hSession = WinHttpOpen(  L"A WinHTTP Example Program/1.0", 
10                              WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
11                              WINHTTP_NO_PROXY_NAME, 
12                              WINHTTP_NO_PROXY_BYPASS, 0);
13 
14     // Specify an HTTP server.
15     if (hSession)
16         hConnect = WinHttpConnect( hSession, L"www.wingtiptoys.com",
17                                    INTERNET_DEFAULT_HTTP_PORT, 0);
18 
19     // Create an HTTP Request handle.
20     if (hConnect)
21         hRequest = WinHttpOpenRequest( hConnect, L"PUT", 
22                                        L"/writetst.txt", 
23                                        NULL, WINHTTP_NO_REFERER, 
24                                        WINHTTP_DEFAULT_ACCEPT_TYPES, 
25                                        0);
26 
27     // Send a Request.
28     if (hRequest) 
29         bResults = WinHttpSendRequest( hRequest, 
30                                        WINHTTP_NO_ADDITIONAL_HEADERS,
31                                        0, WINHTTP_NO_REQUEST_DATA, 0, 
32                                        (DWORD)strlen(pszData), 0);
33 
34     // Write data to the server.
35     if (bResults)
36         bResults = WinHttpWriteData( hRequest, pszData, 
37                                      (DWORD)strlen(pszData), 
38                                      &dwBytesWritten);
39 
40     // End the request.
41     if (bResults)
42         bResults = WinHttpReceiveResponse( hRequest, NULL);
43 
44     // Report any errors.
45     if (!bResults)
46         printf("Error %d has occurred.\n",GetLastError());
47 
48 
49     // Close any open handles.
50     if (hRequest) WinHttpCloseHandle(hRequest);
51     if (hConnect) WinHttpCloseHandle(hConnect);
52     if (hSession) WinHttpCloseHandle(hSession);

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM