转载: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: %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: %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. ",GetLastError()); 47 48 49 // Close any open handles. 50 if (hRequest) WinHttpCloseHandle(hRequest); 51 if (hConnect) WinHttpCloseHandle(hConnect); 52 if (hSession) WinHttpCloseHandle(hSession);