• 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: 
    %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);
  • 相关阅读:
    CountDownLatch, CyclicBarrier, Semaphore
    工具类中使用@Autowired失败问题
    可重入锁(递归锁)
    读写锁
    自旋锁
    加入BLOG
    控制字符串的超长部分用省略号表示
    java常见面试题总结
    maven打包不运行test脚本的命令
    DataGrip使用教程
  • 原文地址:https://www.cnblogs.com/Toya/p/14927913.html
Copyright © 2020-2023  润新知