實(shí)現(xiàn)HTTP訪問的流程包括以下幾步:?
1, 首先我們打開一個Session獲得一個HINTERNET session句柄;?
2, 然后我們使用這個session句柄與服務(wù)器連接得到一個HINTERNET connect句柄;?
3, 然后我們使用這個connect句柄來打開Http請求得到一個HINTERNET request句柄;?
4, 這時我們就可以使用這個request句柄來發(fā)送數(shù)據(jù)與讀取從服務(wù)器返回的數(shù)據(jù);?
5, 最后依次關(guān)閉request,connect,session句柄。
微軟提供了兩套http訪問的接口:WinHTTP和WinINet。WinHTTP比WinINet更加安全和健壯,可以認(rèn)為WinHTTP是WinINet的升級版本。這兩套API包含了很多相似的函數(shù)與宏定義,訪問的流程也是完全類似的(上述5步)。本文主要通過WinHTTP實(shí)現(xiàn)post請求方法,嚴(yán)格按照上述5個步驟給大家進(jìn)行講解。?
又由于我所接收到的數(shù)據(jù)是UTF8而不是ASCII碼,因此一開始接收到的數(shù)據(jù)存在亂碼。在下述代碼中我會詳細(xì)解釋出現(xiàn)亂碼的原因以及如何解決。?
好,小二,上代碼!
---------------------?
#include "stdafx.h"
#include "jsonparser.h"#include <string>
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")int _tmain(int argc, _TCHAR* argv[])
{HINTERNET hSession = NULL;HINTERNET hConnect = NULL;HINTERNET hRequest = NULL;//1. 初始化一個WinHTTP-session句柄,參數(shù)1為此句柄的名稱hSession = WinHttpOpen(L"csdn@elaine_bao", NULL, NULL, NULL, NULL);if (hSession == NULL) {cout<<"Error:Open session failed: "<<GetLastError()<<endl;return -1;}//2. 通過上述句柄連接到服務(wù)器,需要指定服務(wù)器IP和端口號。若連接成功,返回的hConnect句柄不為NULLhConnect = WinHttpConnect(hSession, L"192.168.50.112", (INTERNET_PORT)8080, 0);if (hConnect == NULL) {cout << "Error:Connect failed: " << GetLastError()<<endl;return -1;}//3. 通過hConnect句柄創(chuàng)建一個hRequest句柄,用于發(fā)送數(shù)據(jù)與讀取從服務(wù)器返回的數(shù)據(jù)。hRequest = WinHttpOpenRequest(hConnect, L"Post", L"getServiceInfo", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);//其中參數(shù)2表示請求方式,此處為Post;參數(shù)3:給定Post的具體地址,如這里的具體地址為http://192.168.50.112/getServiceInfoif (hRequest == NULL) {cout << "Error:OpenRequest failed: " << GetLastError() << endl;return -1;}//4-1. 向服務(wù)器發(fā)送post數(shù)據(jù)//(1) 指定發(fā)送的數(shù)據(jù)內(nèi)容string data = "This is my data to be sent"; const void *ss = (const char *)data.c_str();//(2) 發(fā)送請求BOOL bResults;bResults = WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, const_cast<void*>(ss), data.length(), data.length(), 0);if (!bResults){cout << "Error:SendRequest failed: " << GetLastError() << endl;return -1;}else{//(3) 發(fā)送請求成功則準(zhǔn)備接受服務(wù)器的response。注意:在使用 WinHttpQueryDataAvailable和WinHttpReadData前必須使用WinHttpReceiveResponse才能access服務(wù)器返回的數(shù)據(jù)bResults = WinHttpReceiveResponse(hRequest, NULL);}//4-2. 獲取服務(wù)器返回數(shù)據(jù)的header信息。這一步我用來獲取返回數(shù)據(jù)的數(shù)據(jù)類型。LPVOID lpHeaderBuffer = NULL;DWORD dwSize = 0; if (bResults){//(1) 獲取header的長度WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF,WINHTTP_HEADER_NAME_BY_INDEX, NULL,&dwSize, WINHTTP_NO_HEADER_INDEX);//(2) 根據(jù)header的長度為buffer申請內(nèi)存空間if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){lpHeaderBuffer = new WCHAR[dwSize / sizeof(WCHAR)];//(3) 使用WinHttpQueryHeaders獲取header信息bResults = WinHttpQueryHeaders(hRequest,WINHTTP_QUERY_RAW_HEADERS_CRLF,WINHTTP_HEADER_NAME_BY_INDEX,lpHeaderBuffer, &dwSize,WINHTTP_NO_HEADER_INDEX);}}printf("Header contents: \n%S", lpHeaderBuffer);//解析上述header信息會發(fā)現(xiàn)服務(wù)器返回數(shù)據(jù)的charset為uft-8。這意味著后面需要對獲取到的raw data進(jìn)行寬字符轉(zhuǎn)換。一開始由于沒有意識到需要進(jìn)行轉(zhuǎn)換所以得到的數(shù)據(jù)都是亂碼。//出現(xiàn)亂碼的原因是:HTTP在傳輸過程中是二值的,它并沒有text或者是unicode的概念。HTTP使用7bit的ASCII碼作為HTTP headers,但是內(nèi)容是任意的二值數(shù)據(jù),需要根據(jù)header中指定的編碼方式來描述它(通常是Content-Type header).//因此當(dāng)你接收到原始的HTTP數(shù)據(jù)時,先將其保存到char[] buffer中,然后利用WinHttpQueryHearders()獲取HTTP頭,得到內(nèi)容的Content-Type,這樣你就知道數(shù)據(jù)到底是啥類型的了,是ASCII還是Unicode或者其他。//一旦你知道了具體的編碼方式,你就可以通過MultiByteToWideChar()將其轉(zhuǎn)換成合適編碼的字符,存入wchar_t[]中。//關(guān)于亂碼的解決方案請看4-4//4-3. 獲取服務(wù)器返回數(shù)據(jù)LPSTR pszOutBuffer = NULL;DWORD dwDownloaded = 0; //實(shí)際收取的字符數(shù)wchar_t *pwText = NULL;if (bResults){do{//(1) 獲取返回數(shù)據(jù)的大小(以字節(jié)為單位)dwSize = 0;if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;break;} if (!dwSize) break; //數(shù)據(jù)大小為0 //(2) 根據(jù)返回數(shù)據(jù)的長度為buffer申請內(nèi)存空間pszOutBuffer = new char[dwSize + 1];if (!pszOutBuffer){cout<<"Out of memory."<<endl;break;}ZeroMemory(pszOutBuffer, dwSize + 1); //將buffer置0//(3) 通過WinHttpReadData讀取服務(wù)器的返回數(shù)據(jù)if (!WinHttpReadData(hRequest,pszOutBuffer, dwSize, &dwDownloaded)){cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;}if (!dwDownloaded)break;} while (dwSize > 0);//4-4. 將返回數(shù)據(jù)轉(zhuǎn)換成UTF8DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0); //返回原始ASCII碼的字符數(shù)目 pwText = new wchar_t[dwNum]; //根據(jù)ASCII碼的字符數(shù)分配UTF8的空間MultiByteToWideChar(CP_UTF8, 0, pszOutBuffer, -1, pwText, dwNum); //將ASCII碼轉(zhuǎn)換成UTF8printf("Received contents: \n%S", pwText);}//5. 依次關(guān)閉request,connect,session句柄if (hRequest) WinHttpCloseHandle(hRequest);if (hConnect) WinHttpCloseHandle(hConnect);if (hSession) WinHttpCloseHandle(hSession);return 0;
}
?
總結(jié)
以上是生活随笔為你收集整理的C++编程笔记:使用WinHTTP实现HTTP访问(解决接收UTF8数据乱码问题)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。