这段代码也是经历我的“千锤百炼”,用到了我学到的很多东西。比如智能指针等等
还望各位多多指教
#pragma once
#include <string>
#include <sstream>
#include <memory>
#include <vector>
#include <map>
#include <winhttp.h>
class NetworkReply;
using Header = std::multimap <std::string, std::string>;
using HString = std::string;
using WString = std::wstring;
class Headers
{
friend class NetworkReply;
public:
Headers() {}
Headers(const Header& strMap)
{
for (auto& i : strMap)strHeader.emplace(i.first, i.second);
}
Headers(const char* strText, const char* strValue)
{
strHeader.emplace(strText, strValue);
}
void SetHandle(Header& strMap)
{
strHeader = strMap;
}
void setRawHeader(std::string strText, std::string strValue)
{
strHeader.emplace(strText, strValue);
}
operator bool()const
{
return strHeader.empty()==true;
}
~Headers() {}
private:
Header strHeader;
};
enum NetworkError { ERROR_NOERROR, ERROR_CRACK, ERROR_CONNECT, ERROR_REQUEST, ERROR_ADDHEADER, ERROR_SEND, ERROR_READ };
//返回值
class NetworkReply
{
public:
NetworkReply();
~NetworkReply();
bool CrackURL(const char* strUrl);//解析url
bool Connect(HINTERNET hSession);
bool Request(bool Mode);//post get
bool AddHeaders(const Headers& header);//添加头
bool SendRequest(const char* strData);//发送
bool readAll();
WCHAR* GetHeader();//获取buffer
char* GetBuffer()const;
HString GetUtf8Buffer() const;
NetworkError Error() const;
//分割头部到map
Headers rawHeaderList();
unsigned int GetSize() const
{
return responData->size();
}
protected:
WCHAR resHeader[4096];
std::shared_ptr< std::vector<char>> responData = nullptr;
private:
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
NetworkError dwError;
bool HttpMode = true;
unsigned int m_dwServiceType;//连接类型
INTERNET_PORT m_Port;//端口
WCHAR m_strHostName[256],
m_stUrlPath[256];
};
class CHttpClient
{
public:
CHttpClient();
~CHttpClient();
NetworkReply HttpGet(const char* strUrl, Headers& HttpHandle);
NetworkReply HttpPost(const char* strUrl, Headers& HttpHandle, const char* strData);
void SetTimeOut(int nTime);
void SetProxy(LPCWSTR strText);
protected:
private:
HINTERNET hSession;
};
#include "pch.h"
#include "CHttpClient.h"
#pragma comment(lib, "winhttp.lib")
void ConvertUtf8ToGBK(std::string& str)
{
std::string strtemp;
int len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)str.c_str(), -1, NULL, 0);
WCHAR* wszGBK = new WCHAR[len];
memset(wszGBK, 0, len * 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)str.c_str(), -1, wszGBK, len);
len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
char* szGBK = new char[len];
memset(szGBK, 0, len);
WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
str = szGBK;
delete[] szGBK;
delete[] wszGBK;
}
void ConvertGBKToUtf8(std::string& strGBK)
{
int len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)strGBK.c_str(), -1, NULL, 0);
WCHAR* wszUtf8 = new WCHAR[len];
memset(wszUtf8, 0, len * 2);
MultiByteToWideChar(CP_ACP, 0, (LPCSTR)strGBK.c_str(), -1, wszUtf8, len);
len = WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, NULL, 0, NULL, NULL);
char* szUtf8 = new char[len];
memset(szUtf8, 0, len);
WideCharToMultiByte(CP_UTF8, 0, wszUtf8, -1, szUtf8, len, NULL, NULL);
strGBK = szUtf8;
delete[] szUtf8;
delete[] wszUtf8;
}
std::wstring s2ws(const std::string& s)
{
int len;
int slength = (int)s.length() + 1;
len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0);
wchar_t* buf = new wchar_t[len];
MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
std::wstring r(buf);
delete[] buf;
return r;
}
CHttpClient::CHttpClient() :hSession(NULL)
{
hSession = ::WinHttpOpen(L"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36", NULL, NULL, NULL, NULL);
}
CHttpClient::~CHttpClient()
{
if (hSession)
{
WinHttpCloseHandle(hSession);
}
}
NetworkReply CHttpClient::HttpGet(const char* strUrl, Headers& HttpHandle)
{
NetworkReply lReply;
if (lReply.CrackURL(strUrl))
{
if (lReply.Connect(hSession))
{
if (lReply.Request(true))
{
HttpHandle.setRawHeader("Accept", "*/*");
HttpHandle.setRawHeader("Accept-Language", "zh-CN,zh;q=0.9");
if (lReply.AddHeaders(HttpHandle))
{
if (lReply.SendRequest(""))
{
lReply.readAll();
}
}
}
}
}
return lReply;
}
NetworkReply CHttpClient::HttpPost(const char* strUrl, Headers& HttpHandle, const char* strData)
{
NetworkReply lReply;
if (lReply.CrackURL(strUrl))
{
if (lReply.Connect(hSession))
{
if (lReply.Request(false))
{
HttpHandle.setRawHeader("Accept", "*/*");
HttpHandle.setRawHeader("Accept-Language", "zh-CN,zh;q=0.9");
HttpHandle.setRawHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
if (lReply.AddHeaders(HttpHandle))
{
if (lReply.SendRequest(strData))
{
lReply.readAll();
}
}
}
}
}
return lReply;
}
void CHttpClient::SetTimeOut(int nTime)
{
::WinHttpSetTimeouts(hSession, 5000, 5000, 5000, 5000 * 2);
}
void CHttpClient::SetProxy(LPCWSTR strText)
{
WINHTTP_PROXY_INFO proxy = { 0 };
proxy.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
proxy.lpszProxy = lstrcatW(L"http://", strText);
WinHttpSetOption(hSession, WINHTTP_OPTION_PROXY, &proxy, sizeof(proxy));
}
NetworkReply::NetworkReply()
:hConnect(NULL), hRequest(NULL)
{
dwError = NetworkError::ERROR_NOERROR;
memset(m_strHostName, 0L, 256);
memset(m_stUrlPath, 0L, 256);
memset(resHeader, 0L, 4096);
}
NetworkReply::~NetworkReply()
{
if (hConnect)
{
WinHttpCloseHandle(hConnect);
}
if (hRequest)
{
WinHttpCloseHandle(hRequest);
}
}
bool NetworkReply::CrackURL(const char* strUrl)
{
URL_COMPONENTS urlComp;
DWORD dwUrlLen = 0;
ZeroMemory(&urlComp, sizeof(urlComp));
urlComp.dwStructSize = sizeof(urlComp);
urlComp.lpszHostName = m_strHostName;
urlComp.lpszUrlPath = m_stUrlPath;
urlComp.dwSchemeLength = (DWORD)-1;
urlComp.dwExtraInfoLength = (DWORD)-1;
urlComp.dwHostNameLength = 256;
urlComp.dwUrlPathLength = 256;
auto strText = s2ws(strUrl);
if (WinHttpCrackUrl(strText.c_str(), strText.length(), 0, &urlComp))
{
lstrcatW(m_stUrlPath, urlComp.lpszExtraInfo);
m_Port = urlComp.nPort;
m_dwServiceType = urlComp.nScheme;
return true;
}
dwError = NetworkError::ERROR_CRACK;
return false;
}
bool NetworkReply::Connect(HINTERNET hSession)
{
hConnect = WinHttpConnect(hSession, m_strHostName, m_Port, 0);
if (hConnect == NULL)
{
dwError = NetworkError::ERROR_CONNECT;
return false;
}
return true;
}
bool NetworkReply::Request(bool Mode)
{
HttpMode = Mode;
auto Ver = HttpMode ? L"GET" : L"POST";
if (m_dwServiceType == 1)
{
hRequest = WinHttpOpenRequest(hConnect, Ver, m_stUrlPath, L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, NULL);
}
else
{
hRequest = WinHttpOpenRequest(hConnect, Ver, m_stUrlPath, L"HTTP/1.1", WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
DWORD dwFlags = SECURITY_FLAG_IGNORE_UNKNOWN_CA | SECURITY_FLAG_IGNORE_CERT_WRONG_USAGE |
SECURITY_FLAG_IGNORE_CERT_CN_INVALID | SECURITY_FLAG_IGNORE_CERT_DATE_INVALID;
WinHttpSetOption(hRequest, WINHTTP_OPTION_SECURITY_FLAGS, &dwFlags, sizeof(dwFlags));
}
if (hRequest == NULL)
{
dwError = NetworkError::ERROR_REQUEST;
return false;
}
return true;
}
bool NetworkReply::AddHeaders(const Headers& header)//需要测试
{
if (header)
{
return true;
}
for (auto& i : header.strHeader)
{
auto strText = s2ws(i.first + ": " + i.second + "\r\n");
if (!WinHttpAddRequestHeaders(hRequest, strText.c_str(), strText.length(), WINHTTP_ADDREQ_FLAG_ADD))
{
dwError = NetworkError::ERROR_ADDHEADER;
return false;
}
}
return true;
}
bool NetworkReply::SendRequest(const char* strData)
{
if (HttpMode == true) {
if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, WINHTTP_NO_REQUEST_DATA, NULL, NULL, NULL))
{
return true;
}
}
else {
std::string str = strData;
ConvertGBKToUtf8(str);
if (WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, NULL, (LPVOID)str.c_str(), str.length(), str.length(), 0))
{
return true;
}
}
dwError = NetworkError::ERROR_SEND;
return false;
}
bool NetworkReply::readAll()
{
bool bResults = WinHttpReceiveResponse(hRequest, NULL);
if (bResults)
{
DWORD dwSize = 0;
WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, NULL, &dwSize, WINHTTP_NO_HEADER_INDEX);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
//(3) 使用WinHttpQueryHeaders获取header信息
bResults = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_RAW_HEADERS_CRLF, WINHTTP_HEADER_NAME_BY_INDEX, resHeader, &dwSize, WINHTTP_NO_HEADER_INDEX);
if (bResults)
{
std::vector<char> tempBuffer;
DWORD dwDownloaded = 0; //实际收取的字符数
DWORD dwOffset = 0;
do {
dwSize = 0;
bResults = WinHttpQueryDataAvailable(hRequest, &dwSize);
if (!bResults) {
break;
}
if (!dwSize)
{
break;
}
//(3) 通过WinHttpReadData读取服务器的返回数据
tempBuffer.resize(dwOffset + dwSize + 1);
bResults = WinHttpReadData(hRequest, &tempBuffer[dwOffset], dwSize, &dwDownloaded);
if (!bResults) {
break;
}
dwOffset += dwSize;
if (!dwDownloaded)
break;
} while (dwSize > 0);
responData = std::make_shared<std::vector<char>>(tempBuffer);
}
}
}
if (bResults == false)
{
dwError = NetworkError::ERROR_READ;
}
return bResults;
}
WCHAR* NetworkReply::GetHeader()
{
return resHeader;
}
char* NetworkReply::GetBuffer() const
{
return responData->data();
}
HString NetworkReply::GetUtf8Buffer() const
{
HString str = responData->data();
ConvertUtf8ToGBK(str);
return str;
}
NetworkError NetworkReply::Error() const
{
return dwError;
}
Headers NetworkReply::rawHeaderList()
{
return Headers();
}