windows WinHttp

WinInet和WinHttp是windows平台下提供了两套独立的网络库,按照微软官方的说法, WinInet的优势在于client-端的应用,而WinHttp更适用于server-端编程。从名称上我们可以看出WinHttp在Http协议应用方面要比WinInet更加专业,WinInet支持的协议包括Gopher\HTTP\HTTPS\FTP较为杂乱,而WinHttp库专门是为HTTP\HTTPS服务的。

在一个Session之上可以存在多个Connection对象,每个Connection对应于一个目标服务器和端口号。在一个Connection之上可以存在多个Request对象,每个Request对应于Connection之上的URI资源地址以及HTTP动作(GET或POST等)。

使用winhttp需要调用winhttp库(windows系统自带winhttp.lib和winhttp.dll)直接

#include <winhttp.h>
#pragma comment(lib,"winhttp.lib")

初始化winhttp

在使用WinHttp连接服务器之前,需要使用WinHttpOpen来进行初始化。WinHttpOpen将创建一个维护Http会话细节的会话环境(Session Context),并返回一个session handle。使用该句柄,WinHttpConnect可以连接到目标Http(Https)服务器。

WINHTTPAPI
HINTERNET
WINAPI
WinHttpOpen
(
    __in_opt LPCWSTR pszAgentW,
    __in DWORD dwAccessType,
    __in_opt LPCWSTR pszProxyW,
    __in_opt LPCWSTR pszProxyBypassW,
    __in DWORD dwFlags
);

见:WinHttpOpen function (winhttp.h) - Win32 apps | Microsoft Docs

如:

HINTERNET hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

在与服务器交互之前, 必须用调用WinHttpOpen进行初始化,WinHttpOpen创建一个会话,并返回该会话的句柄,接着有了这个句柄.

BOOL WinHttpSetTimeouts

(

[in] HINTERNET hInternet,

[in] int nResolveTimeout,

[in] int nConnectTimeout,

[in] int nSendTimeout,

[in] int nReceiveTimeout

);

见:WinHttpSetTimeouts function (winhttp.h) - Win32 apps | Microsoft Docs

如:

HINTERNET hSession = WinHttpOpen(L"A WinHTTP Example Program/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);

if (hSession)

{

// Use WinHttpSetTimeouts to set a new time-out values.

if (!WinHttpSetTimeouts( hSession, 10000, 10000, 10000, 10000))

printf( "Error %u in WinHttpSetTimeouts.\n", GetLastError());

BOOL WinHttpSetOption(
  [in] HINTERNET hInternet,
  [in] DWORD     dwOption,
  [in] LPVOID    lpBuffer,
  [in] DWORD     dwBufferLength
);

见:WinHttpSetOption function (winhttp.h) - Win32 apps | Microsoft Docs

参数的值见:Option Flags (Winhttp.h) - Win32 apps | Microsoft Docs

如:

DWORD dwFlags = SECURITY_FLAG_SECURE |
            SECURITY_FLAG_IGNORE_UNKNOWN_CA |
            SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE |
            SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
            SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
WinHttpSetOption(m_hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags));

BOOL WinHttpQueryOption(
  [in]      HINTERNET hInternet,
  [in]      DWORD     dwOption,
  [out]     LPVOID    lpBuffer,
  [in, out] LPDWORD   lpdwBufferLength
);

见:WinHttpQueryOption function (winhttp.h) - Win32 apps | Microsoft Docs

其和上面的WinHttpSetOption函数基本一致。

连接目标服务器

需要指定服务器IP和端口号。

WINHTTPAPI
HINTERNET
WINAPI
WinHttpConnect
(
    IN HINTERNET hSession,
    IN LPCWSTR pswzServerName,
    IN INTERNET_PORT nServerPort,
    IN DWORD dwReserved             //该值必须是0
);

pswzServerName:指向包含 HTTP 服务器的主机名的 以 null 结尾的字符串的指针

见:WinHttpConnect function (winhttp.h) - Win32 apps | Microsoft Docs

hConnect = WinHttpConnect( hSession, L"www.microsoft.com",INTERNET_DEFAULT_HTTPS_PORT, 0);

WinHttpConnect就能指定一个目标服务器.
注意:调用了WinHttpConnect并不意味着和服务器建立了真正的连接.

打开请求

在调用WinHttpOpenRequest时并不会向服务器发送请求。指定是get还是post以及网址后面的目录

WINHTTPAPI
HINTERNET
WINAPI
WinHttpOpenRequest
(
    IN HINTERNET hConnect,
    IN LPCWSTR pwszVerb,            //GET 或者是POST
    IN LPCWSTR pwszObjectName,        //网址后面的目录
    IN LPCWSTR pwszVersion,              如HTTP/1.1
    IN LPCWSTR pwszReferrer OPTIONAL,    如WINHTTP_NO_REFERER
    IN LPCWSTR FAR * ppwszAcceptTypes OPTIONAL,   如                                   
    IN DWORD dwFlags
);

pwszObjectName: 指向包含指定 HTTP 谓词的目标资源名称的字符串的指针。 这通常是文件名、可执行模块或搜索说明符。

见:WinHttpOpenRequest function (winhttp.h) - Win32 apps | Microsoft Docs

hRequest = WinHttpOpenRequest(hConnect, L"Get", L"/GetSanyNestTask?token=123", NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0);

添加请求头部

即设置请求协议头信息

BOOLAPI
WinHttpAddRequestHeaders
(
    IN HINTERNET hRequest,
    IN LPCWSTR pwszHeaders,
    IN DWORD dwHeadersLength,
    IN DWORD dwModifiers
);

pwszHeaders: 指向字符串变量的指针,该变量包含要追加到请求的标头。 每个标头(最后一个除外)都必须由回车/换行符 (CR/LF) 终止。

见:WinHttpAddRequestHeaders function (winhttp.h) - Win32 apps | Microsoft Docs

	LPCWSTR header = _T("Content-type: application/json; charset=utf-8/r/n");
	SIZE_T len = lstrlenW(header);
	bResults = WinHttpAddRequestHeaders(hRequest, header, DWORD(len), WINHTTP_ADDREQ_FLAG_ADD);
	if(!bResults)
	{
		cout << "Error:Headers failed: " << endl;
		return -1;
	}
向服务器发送请求

BOOLAPI
WinHttpSendRequest
(
    IN HINTERNET hRequest,
    IN LPCWSTR pwszHeaders OPTIONAL, //该字符串包含要追加到请求的其他标头
    IN DWORD dwHeadersLength,
    IN LPVOID lpOptional OPTIONAL,//该缓冲区包含要紧接在请求标头之后发送的任何可选数 据此参数通常用于 POST 和 PUT 操作
    IN DWORD dwOptionalLength,
    IN DWORD dwTotalLength,
    IN DWORD_PTR dwContext
);

见:WinHttpSendRequest function (winhttp.h) - Win32 apps | Microsoft Docs

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

 WinHttpOpenRequest函数打开一个HTTP请求并返回一个HINTERNET 句柄,该句柄在接下来的步骤中会用到 , WinHttpOpenRequest并不会向服务器发送请求, 实际上是WinHttpSendRequest通过网络与服务器建立连接并发送请求.

向服务器发送数据

BOOLAPI
WinHttpWriteData
(
    IN HINTERNET hRequest,
    IN LPCVOID lpBuffer,
    IN DWORD dwNumberOfBytesToWrite,
    OUT LPDWORD lpdwNumberOfBytesWritten
);

见:WinHttpWriteData function (winhttp.h) - Win32 apps | Microsoft Docs

// Send a Request.

if (hRequest) bResults = WinHttpSendRequest( hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, (DWORD)strlen(pszData), 0);

// Write data to the server.

if (bResults) bResults = WinHttpWriteData( hRequest, pszData, (DWORD)strlen(pszData), &dwBytesWritten);

// End the request.

if (bResults) bResults = WinHttpReceiveResponse( hRequest, NULL);

获取响应

WINHTTPAPI
BOOL
WINAPI
WinHttpReceiveResponse
(
    IN HINTERNET hRequest,
    IN LPVOID lpReserved        //必须是NULL
);

见:WinHttpReceiveResponse function (winhttp.h) - Win32 apps | Microsoft Docs

An application must call WinHttpReceiveResponse before it can use WinHttpQueryDataAvailable and WinHttpReadData to access the response entity body (if any).

所以其调用放在WinHttpSendRequest、WinHttpWriteData后,WinHttpQueryHeadersWinHttpQueryDataAvailable前。

如bResults = WinHttpReceiveResponse( hRequest, NULL);

获取响应头信息

应用程序检索HTTP请求的信息,主要是得到响应头信息,包括响应头的长度

BOOL WinHttpQueryHeaders(
  [in]           HINTERNET hRequest,
  [in]           DWORD     dwInfoLevel,
  [in, optional] LPCWSTR   pwszName,
  [out]          LPVOID    lpBuffer,
  [in, out]      LPDWORD   lpdwBufferLength,
  [in, out]      LPDWORD   lpdwIndex
);

见:WinHttpQueryHeaders function (winhttp.h) - Win32 apps | Microsoft Docs

第二个参数dwInfoLevel 的设置见:Query Info Flags (Winhttp.h) - Win32 apps | Microsoft Docs

如:

    int http_code = 0;
    wchar_t szBuffer[24] = { 0 };
    DWORD dwSize = 24 * sizeof(wchar_t);
    if (::WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_STATUS_CODE, WINHTTP_HEADER_NAME_BY_INDEX, szBuffer, &dwSize, WINHTTP_NO_HEADER_INDEX)) {
        wchar_t *p = NULL;
        http_code = wcstoul(szBuffer, &p, 10);
    }

从服务器读取到响应报文的长度

获取响应报文的长度

BOOLAPI
WinHttpQueryDataAvailable
(
    IN HINTERNET hRequest,
    OUT __out_data_source(NETWORK) LPDWORD lpdwNumberOfBytesAvailable OPTIONAL
);

见:WinHttpQueryDataAvailable function (winhttp.h) - Win32 apps | Microsoft Docs

dwSize = 0; if (!WinHttpQueryDataAvailable( hRequest, &dwSize)) printf( "Error %u in WinHttpQueryDataAvailable.\n", GetLastError());

从服务器读取响应报文

BOOLAPI
WinHttpReadData
(
    IN HINTERNET hRequest,
    IN __out_data_source(NETWORK) LPVOID lpBuffer,
    IN DWORD dwNumberOfBytesToRead,
    OUT LPDWORD lpdwNumberOfBytesRead
);

见:WinHttpReadData function (winhttp.h) - Win32 apps | Microsoft Docs

ZeroMemory(pszOutBuffer, dwSize+1);

if (!WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded))

{ printf( "Error %u in WinHttpReadData.\n", GetLastError()); }

关闭winhttp资源

BOOLAPI
WinHttpCloseHandle
(
    IN HINTERNET hInternet
);

见:WinHttpCloseHandle function (winhttp.h) - Win32 apps | Microsoft Docs

http://xxx.yyy.zzz:8324/urlpath?pk1=pv1&pk2=pk2

get方式:

  1. xxx.yyy.zzz:8324 在WinHttpConnect的参数pswzServerName设置
  2. 如果需要传参数(pk1=pv1&pk2=pk2)的话,参数只能和urlpath合在一起,通过设置WinHttpOpenRequest的参数pwszObjectName传递。

Post方式:

  1. xxx.yyy.zzz:8324 在WinHttpConnect的参数pswzServerName设置
  2. urlpath在WinHttpOpenRequest的参数pwszObjectName设置。
  3. 设置协议头WinHttpAddRequestHeaders

如果协议头是Content-type: application/x-www-form-urlencoded

   std::wstring wstrHeader[] = { L"Content-type: application/x-www-form-urlencoded\r\n"};

    for ( size_t i = 0; i < ARRAYSIZE(wstrHeader); i++ ) {

        WinHttpAddRequestHeaders(hRequest, wstrHeader[i].c_str(), wstrHeader[i].length(), WINHTTP_ADDREQ_FLAG_ADD);

}

如果协议头是Content-type: multipart/form-data

LPCWSTR additionalHeaders = L"nContent-Type: multipart/form-data; boundary=----------------------------346435246262465368257857\r\n";

WinHttpAddRequestHeaders(hRequest, additionalHeaders, -1L, WINHTTP_ADDREQ_FLAG_ADD);

4.调WinHttpSendRequest( m_hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, dwTotal, 0)  dwTotal为需要传输数据的大小

如:Content-type: application/x-www-form-urlencoded

dwTotal的大小为:键值对(具体为?pk1=pv1&pk2=pk2)的长度

5.调WinHttpWriteData传输实际的数据。

完整一个例子

DWORD dwSize = 0;
    DWORD dwDownloaded = 0;
    LPSTR pszOutBuffer;
    BOOL  bResults = FALSE;
    HINTERNET  hSession = NULL, 
               hConnect = NULL,
               hRequest = NULL;

    // Use WinHttpOpen to obtain a session handle.
    hSession = WinHttpOpen( L"WinHTTP Example/1.0",  
                            WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
                            WINHTTP_NO_PROXY_NAME, 
                            WINHTTP_NO_PROXY_BYPASS, 0);

    // Specify an HTTP server.
    if (hSession)
        hConnect = WinHttpConnect( hSession, L"www.microsoft.com",
                                   INTERNET_DEFAULT_HTTPS_PORT, 0);

    // Create an HTTP request handle.
    if (hConnect)
        hRequest = WinHttpOpenRequest( hConnect, L"GET", NULL,
                                       NULL, WINHTTP_NO_REFERER, 
                                       WINHTTP_DEFAULT_ACCEPT_TYPES, 
                                       WINHTTP_FLAG_SECURE);

    // Send a request.
    if (hRequest)
        bResults = WinHttpSendRequest( hRequest,
                                       WINHTTP_NO_ADDITIONAL_HEADERS,
                                       0, WINHTTP_NO_REQUEST_DATA, 0, 
                                       0, 0);

 
    // End the request.
    if (bResults)
        bResults = WinHttpReceiveResponse( hRequest, NULL);

    // Continue to verify data until there is nothing left.
    if (bResults)
        do 
        {

            // Verify available data.
            dwSize = 0;
            if (!WinHttpQueryDataAvailable( hRequest, &dwSize))
                printf( "Error %u in WinHttpQueryDataAvailable.\n",
                        GetLastError());

            // Allocate space for the buffer.
            pszOutBuffer = new char[dwSize+1];
            if (!pszOutBuffer)
            {
                printf("Out of memory\n");
                dwSize=0;
            }
            else
            {
                // Read the Data.
                ZeroMemory(pszOutBuffer, dwSize+1);

                if (!WinHttpReadData( hRequest, (LPVOID)pszOutBuffer, 
                                      dwSize, &dwDownloaded))
                    printf( "Error %u in WinHttpReadData.\n", GetLastError());
                else
                    printf( "%s\n", pszOutBuffer);
            
                // Free the memory allocated to the buffer.
                delete [] pszOutBuffer;
            }

        } while (dwSize > 0);


    // Report any errors.
    if (!bResults)
        printf( "Error %d has occurred.\n", GetLastError());

    // Close open handles.
    if (hRequest) WinHttpCloseHandle(hRequest);
    if (hConnect) WinHttpCloseHandle(hConnect);
    if (hSession) WinHttpCloseHandle(hSession);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值