Notas de programación de C ++: use WinHTTP para lograr el acceso HTTP (resuelva el problema de recibir datos UTF8 confusos)

El proceso de implementación del acceso HTTP incluye los siguientes pasos: 
1. Primero, abrimos una sesión para obtener un identificador de sesión HINTERNET; 
2. Luego usamos este identificador de sesión para conectarnos al servidor para obtener un identificador de conexión HINTERNET; 
3. Luego usar este identificador de conexión para abrir la solicitud Http obtiene un identificador de solicitud HINTERNET; 
4. En este momento, podemos usar este identificador de solicitud para enviar datos y leer los datos devueltos desde el servidor; 
5. Finalmente, cierre la solicitud, la conexión y la sesión. asas a su vez.

Microsoft proporciona dos conjuntos de interfaces de acceso http: WinHTTP y WinINet. WinHTTP es más seguro y robusto que WinINet, y se puede considerar que WinHTTP es una versión mejorada de WinINet. Estas dos API contienen muchas funciones y definiciones de macros similares, y el proceso de acceso es completamente similar (los 5 pasos anteriores). Este artículo implementa principalmente el método de solicitud de publicación a través de WinHTTP y lo explica estrictamente de acuerdo con los cinco pasos anteriores. 
Y debido a que los datos que recibí eran UTF8 en lugar de ASCII, los datos que recibí al principio estaban confusos. En el siguiente código, explicaré en detalle la causa del código ilegible y cómo solucionarlo. 
De acuerdo, Xiao Er, ¡ve al código!
--------------------- 

#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句柄,参数1为此句柄的名称
    hSession = WinHttpOpen(L"csdn@elaine_bao", NULL, NULL, NULL, NULL);
    if (hSession == NULL) {
        cout<<"Error:Open session failed: "<<GetLastError()<<endl;
        return -1;
    }

    //2. 通过上述句柄连接到服务器,需要指定服务器IP和端口号。若连接成功,返回的hConnect句柄不为NULL
    hConnect = WinHttpConnect(hSession, L"192.168.50.112", (INTERNET_PORT)8080, 0);
    if (hConnect == NULL) {
        cout << "Error:Connect failed: " << GetLastError()<<endl;
        return -1;
    }

    //3. 通过hConnect句柄创建一个hRequest句柄,用于发送数据与读取从服务器返回的数据。
    hRequest = WinHttpOpenRequest(hConnect, L"Post", L"getServiceInfo", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);
    //其中参数2表示请求方式,此处为Post;参数3:给定Post的具体地址,如这里的具体地址为http://192.168.50.112/getServiceInfo
    if (hRequest == NULL) {
        cout << "Error:OpenRequest failed: " << GetLastError() << endl;
        return -1;
    }

    //4-1. 向服务器发送post数据
    //(1) 指定发送的数据内容
    string data = "This is my data to be sent"; 
    const void *ss = (const char *)data.c_str();

    //(2) 发送请求
    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) 发送请求成功则准备接受服务器的response。注意:在使用 WinHttpQueryDataAvailable和WinHttpReadData前必须使用WinHttpReceiveResponse才能access服务器返回的数据
        bResults = WinHttpReceiveResponse(hRequest, NULL);
    }

    //4-2. 获取服务器返回数据的header信息。这一步我用来获取返回数据的数据类型。
    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) 根据header的长度为buffer申请内存空间
        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信息会发现服务器返回数据的charset为uft-8。这意味着后面需要对获取到的raw data进行宽字符转换。一开始由于没有意识到需要进行转换所以得到的数据都是乱码。
    //出现乱码的原因是:HTTP在传输过程中是二值的,它并没有text或者是unicode的概念。HTTP使用7bit的ASCII码作为HTTP headers,但是内容是任意的二值数据,需要根据header中指定的编码方式来描述它(通常是Content-Type header).
    //因此当你接收到原始的HTTP数据时,先将其保存到char[] buffer中,然后利用WinHttpQueryHearders()获取HTTP头,得到内容的Content-Type,这样你就知道数据到底是啥类型的了,是ASCII还是Unicode或者其他。
    //一旦你知道了具体的编码方式,你就可以通过MultiByteToWideChar()将其转换成合适编码的字符,存入wchar_t[]中。
    //关于乱码的解决方案请看4-4

    //4-3. 获取服务器返回数据
    LPSTR pszOutBuffer = NULL;
    DWORD dwDownloaded = 0;         //实际收取的字符数
    wchar_t *pwText = NULL;
    if (bResults)
    {
        do
        {
            //(1) 获取返回数据的大小(以字节为单位)
            dwSize = 0;
            if (!WinHttpQueryDataAvailable(hRequest, &dwSize)){
                cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
                break;
            }           
            if (!dwSize)    break;  //数据大小为0                

            //(2) 根据返回数据的长度为buffer申请内存空间
            pszOutBuffer = new char[dwSize + 1];
            if (!pszOutBuffer){
                cout<<"Out of memory."<<endl;
                break;
            }
            ZeroMemory(pszOutBuffer, dwSize + 1);       //将buffer置0

            //(3) 通过WinHttpReadData读取服务器的返回数据
            if (!WinHttpReadData(hRequest,pszOutBuffer, dwSize, &dwDownloaded)){
                cout << "Error:WinHttpQueryDataAvailable failed:" << GetLastError() << endl;
            }
            if (!dwDownloaded)
                break;

        } while (dwSize > 0);

        //4-4. 将返回数据转换成UTF8
        DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, pszOutBuffer, -1, NULL, 0);    //返回原始ASCII码的字符数目       
        pwText = new wchar_t[dwNum];                                                //根据ASCII码的字符数分配UTF8的空间
        MultiByteToWideChar(CP_UTF8, 0, pszOutBuffer, -1, pwText, dwNum);           //将ASCII码转换成UTF8
        printf("Received contents: \n%S", pwText);
    }


    //5. 依次关闭request,connect,session句柄
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);


    return 0;
}

 

Supongo que te gusta

Origin blog.csdn.net/fuweiping/article/details/99315183
Recomendado
Clasificación