相关文章推荐

C/C++的HTTP/HTTPS的GET/POST请求编程

方法一(不使用OpenSSL):

bool CXXX::GetHttpInfo(CString& strIP, CString& strPort)
	//CString strURL= L"http://www.baidu.com/";
	CString strURL= L"https://www.baidu.com/";
	CInternetSession session;//一、建立会话(Session)对象:还可以调用session.SetOption函数设置一些超时选项
	CHttpConnection* pHttpConnection = NULL;
	CHttpFile* pHttpFile = NULL;
	CString strServer, strObject;
	INTERNET_PORT wPort;
	DWORD dwType;
	if (!AfxParseURL(strURL, dwType, strServer, strObject, wPort))
		return false;//URL解析错误
		pHttpConnection = session.GetHttpConnection(strServer, wPort);						//二、连接到Http服务器:
		if (NULL == pHttpConnection)
			return false;
		//pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject);//三、打开Http请求
		//pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL, NULL);//三、打开Http请求
		pHttpFile = pHttpConnection->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1, NULL, NULL, INTERNET_FLAG_SECURE);//三、打开Https请求
		if (NULL == pHttpFile)
			pHttpConnection->Close();
			delete pHttpConnection;
			pHttpConnection = NULL;
			session.Close();
			return false;
		BOOL bRet = pHttpFile->SendRequest();//四、发送Http请求:设置HTTP请求包头、发送数据
		if (!bRet)
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
			pHttpConnection->Close();
			delete pHttpConnection;
			pHttpConnection = NULL;
			session.Close();
			return false;
		DWORD dwRet;
		bRet = pHttpFile->QueryInfoStatusCode(dwRet);//五、查询状态:200-299代表Success;200:URL located,transmission follows
		if (!bRet)
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = NULL;
			pHttpConnection->Close();
			delete pHttpConnection;
			pHttpConnection = NULL;
			session.Close();
			return false;
		if (dwRet == HTTP_STATUS_OK)
			CString strSentence = _T("");
			TCHAR cbContent[65535];
			CString strGetSentence = _T("");
			while (pHttpFile->ReadString(cbContent, 65535))// 读取提交数据后的返回结果
				int nBufferSize = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)cbContent, -1, NULL, 0);
				wchar_t* pBuffer = new wchar_t[nBufferSize + 1];
				MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)cbContent, -1, pBuffer, nBufferSize * sizeof(wchar_t));
				strSentence = pBuffer;
				strGetSentence = strGetSentence + strSentence + char(13) + char(10);
			string strTmp = CStringToString(strGetSentence);
			Json::Reader reader;
			Json::Value root;
			if (reader.parse(strTmp, root))  // reader将Json字符串解析到root,root将包含Json里所有子元素  
				string strMessage;
				strMessage = root["field1"].asString();
				if (strMessage == "0")
					Json::Value data = root["field2"];
					string str1 = data["field2ip"].asString();
					string str2 = data["field2port"].asString();
					strIP = StringToCString(str1);
					strPort = StringToCString(str2);
		if (pHttpFile != NULL)
			pHttpFile->Close();
			delete pHttpFile;
			pHttpFile = 0;
		if (pHttpConnection != NULL)
			pHttpConnection->Close();
			delete pHttpConnection;
			pHttpConnection = 0;
	catch (CInternetException* e)
		CString sError;
		sError.Format(_T("网络错误 : %d"), e->m_dwError);
		GetDlgItem(IDC_STATIC_Allmessage)->SetWindowText(sError);
		pHttpFile->Close();
		delete pHttpFile;
		pHttpFile = 0;
		//关闭网络连接  
		session.Close();
		return false;
	session.Close();
	return true;

方法二(使用OpenSSL):

1、将openssl-0.9.8m文件夹和lib文件夹(libeay32.lib、ssleay32.lib)放到sln目录下,工程属性C/C++下“常规”里“附加包含目录”添加“..\openssl-0.9.8m\include;”。

2、新建OpenSSL接口使用的封装类(见 C/C++: OpenSSL实现https GET POST请求

其他学习链接( 笔记整理--使用openssl编程 - suntl - 博客园

/*
笔者这里有需求,需要用C++实现 https 的GET、POST请求 以及GET下载文件 而且需要实现跨平台 在Linux、Windows都能正常运行。
最好的是 只用一套代码 而不是根据具不同平台 跑不同代码,所以我们得找一个跨平台的支持https协议的库。
都不用想,最好的当然是 OpenSSL啦。也有其他的比如libcurl 啥的,但是libcurl如果要支持https的也要链接OpenSSL了,还不如直接用OpenSSL。
当然这里还有个不错的选择就是使用boost::asio库,但是笔者觉得使用上没有OpenSSL好用,故此没有用boost库。
#pragma once
#include <string>
#include <openssl/ssl.h>
enum REQ_TYPE
    GET_STRING = 0, // GET请求获取响应字符串
    POST_STRING,    // POST请求获得响应字符串
    GET_FILE        // GET请求下载文件
class MyHttpsUtil
    public:
    virtual ~MyHttpsUtil(void);
    static MyHttpsUtil* getInstance();
        https get请求
        return value
            0:成功 strResponse为响应结果 -1:失败,strResponse为错误信息
    int getRequest(const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, std::string& strResponse);
        https post请求
        return value
            0:成功 strResponse为响应结果 -1:失败,strResponse为错误信息
    int postRequest(const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, const std::string& strBody, std::string& strResponse);
        https get请求下载文件
        return value
            0:成功 -1:失败,strErrMsg为错误信息
    int getFile(const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strErrMsg );
private:
    MyHttpsUtil(void);
private:
    // 建立TCP连接套接字
    int tcpConn(const char* pSvrIp, int iSvrPort, int& socket);
        发送数据到https服务器
        参数1:请求类型,决定最后3个参数的作用
        参数2:服务器IP
        参数3:服务器端口
        参数4:uri
        参数5:reqType:1 为POST_STRING时(POST请求) 请求参数,也就是请求体。
        参数6:reqType:2 为GET_FILE时(GET请求下载资源文件) 文件存储路径。
        参数7:reqType:0/1 为GET_STRING/POST_STRING 时(GET/POST请求响应字符串) 响应字符串在strResponse, 出现错误时 错误描述信息在strResponse中。
    int sendDataToSvr(REQ_TYPE reqType, const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strResponse );
    // 组装GET请求数据
    int getGetReqData(const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, std::string& strReqData);
    // 组装POST请求数据
    int getPostReqData(const std::string& strSvrIp, int iSvrPort, 
        const std::string& strUri, const std::string& strBody, std::string& strReqData);
    // 读取响应字符串
    int readResponseToString(SSL* ssl, std::string& strRespData);
    // 读取响应二进制数据到文件
    int readResponseToFile(SSL* ssl, const std::string& strFilePath, std::string& strErrMsg);
};

MyHttpsUtil.cpp文件:

#include "MyHttpsUtil.h"
#ifdef WIN32
#include <winsock.h>
#else
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#endif
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
//OpenSSL 不仅仅是SSL。它可以实现消息摘要、文件的加密和解密、数字证书、数字签名和随机数字。
//OpenSSL 不只是API,它还是一个命令行工具。命令行工具可以完成与API同样的工作,而且更进一步,可以测试SSL服务器和客户机。
#define CODE_SUCCESS 0
#define CODE_FALID -1
#ifdef WIN32//OpenSSL是在SSLeay包的基础上对TSL/SSL的一个免费的执行。
#pragma comment(lib, "..\\lib\\libeay32.lib")
#pragma comment(lib, "..\\lib\\ssleay32.lib")
#endif
MyHttpsUtil::MyHttpsUtil(void)
MyHttpsUtil::~MyHttpsUtil(void)
MyHttpsUtil* MyHttpsUtil::getInstance()
    static MyHttpsUtil httpsClient;
    return &httpsClient;
int MyHttpsUtil::getRequest(const std::string& strSvrIp, int iSvrPort, 
    const std::string& strUri, std::string& strResponse)
    return sendDataToSvr(GET_STRING, strSvrIp, iSvrPort, strUri, "", "", strResponse);
int MyHttpsUtil::postRequest(const std::string& strSvrIp, int iSvrPort, 
    const std::string& strUri, const std::string& strBody, std::string& strResponse)
    return sendDataToSvr(POST_STRING, strSvrIp, iSvrPort, strUri, strBody, "", strResponse);
int MyHttpsUtil::getFile(const std::string& strSvrIp, int iSvrPort, 
    const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strErrMsg)
    return sendDataToSvr(GET_FILE, strSvrIp, iSvrPort, strUri, "", strFilePath, strErrMsg);
int MyHttpsUtil::sendDataToSvr(REQ_TYPE reqType, const std::string& strSvrIp, int iSvrPort, const std::string& strUri, const std::string& strBody, const std::string& strFilePath, std::string& strResponse)
    int iRet = CODE_FALID;
    int socketFd = 0;
    SSL_CTX* ctx = 0;
    SSL* ssl = 0;
        WSADATA wsadata;
        int err=WSAStartup(MAKEWORD(2, 2), &wsadata);
        if (0 != err)
            break;
        char* pSvrIp = NULL;
        struct hostent *pHostent = NULL;
        //pHostent = gethostbyname("www.baidu.com");//不包含https://,只是域名
        pHostent = gethostbyname(strSvrIp.c_str());
        if (pHostent == NULL)
            break;
        pSvrIp = inet_ntoa(*(struct in_addr*)pHostent->h_addr_list[0]);
        // 1.建立TCP连接
        if( tcpConn(pSvrIp, iSvrPort, socketFd) != CODE_SUCCESS)//OpenSSL要求我们建立一条从客户端到服务器的TCP连接。
            break;
        // 2.SSL初始化, 关联Socket到SSL,并建立连接
        SSL_library_init();          //在应用OpenSSL之前,整个库需要通过该函数进行初始化。
	    OpenSSL_add_all_algorithms();//主要加载OpenSSL将会用到的算法。
	    SSL_load_error_strings();    //如果我们想很好的报告查错信息,需要通过该函数来加载错误字符串。否则就不能将OpenSSL的错误映射为字符串。
	    ctx = SSL_CTX_new(SSLv23_client_method()); //我们的首要任务是建立一个上下文SSL_CTX,它会在每次需要创建新的SSL连接时被用来创建一个新的连接对象。这些连接对象用于SSL握手和读写。
        if(ctx == NULL)
            break;
        ssl = SSL_new(ctx);           //当TCP连接创建好以后,创建一个SSL对象来处理这个连接。这个对象需要与套接字绑定。
        if (ssl == NULL)
            break;
        int retCode=SSL_set_fd(ssl, socketFd);//然后使用上面的TCP套接字创建一个SSL套接字。间接绑定:创建一个使用该TCP套接字的BIO对象,SSL绑定到BIO上。
        if (retCode < 0)                      //这个抽象层允许我们通过各种通道来使用OpenSSL而不是套接字。
            break;
        retCode = SSL_connect(ssl);//SSL握手
        if ( retCode != 1)
            int sslErrCode = SSL_get_error(ssl,retCode);
            strResponse = "SSL_connect error,openssl errCode = ";
            char errCode[11] = {0};
            sprintf_s(errCode, "%d", sslErrCode);
            strResponse.append(errCode);
            break;
        std::string strReqData;
        if(GET_FILE == reqType || GET_STRING == reqType)
            getGetReqData(strSvrIp, iSvrPort, strUri, strReqData);
            getPostReqData(strSvrIp, iSvrPort, strUri, strBody, strReqData);
	    // 4.通过SSL发送数据,数据量不大一次write就够了,如果是文件上传就需要循环写了。
	    int writeLen = SSL_write(ssl, strReqData.c_str(), strReqData.length());  
	    if (writeLen <=0)
		    int sslErrCode = SSL_get_error(ssl,writeLen);
            strResponse = "SSL_write error,openssl errCode = ";
            char errCode[11] = {0};
            sprintf_s(errCode, "%d", sslErrCode);
            strResponse.append(errCode);
            break;
	    // 5.读取响应数据  
        int readLen = 0;
        char pHeader[1] = {0};
        int i = 0;
	    // 响应头以\r\n\r\n 结束, 此处判断头是否传输完成
	    while((readLen = SSL_read(ssl, pHeader, 1)) == 1)
		    if(i < 4){
			    if(pHeader[0] == '\r' || pHeader[0] == '\n')
				    if(i >= 4)
					    break;
				    i = 0;
        if( readLen < 0 )
            int sslErrCode = SSL_get_error(ssl,readLen);
            strResponse = "SSL_read error,openssl errCode = ";
            char errCode[11] = {0};
            sprintf_s(errCode, "%d", sslErrCode);
            strResponse.append(errCode);
            break;
        if(GET_FILE == reqType)
            iRet = readResponseToFile(ssl, strFilePath, strResponse);
            iRet = readResponseToString(ssl, strResponse);
    } while (false);
    // 6.关闭socket、断开连接
    if (socket)
#ifdef WIN32
        closesocket(socketFd);
#else
        close(socketFd);
#endif
    if (ctx)
        SSL_CTX_free(ctx);
    if (ssl)
        SSL_shutdown (ssl);
        SSL_free(ssl);
    WSACleanup();
    return iRet;
int MyHttpsUtil::tcpConn(const char* pSvrIp, int iSvrPort, int& socket)
    socket = ::socket(AF_INET,SOCK_STREAM,0);
    if( socket == -1 )
        return CODE_FALID;
    sockaddr_in sa;
    sa.sin_addr.s_addr = inet_addr(pSvrIp);
    sa.sin_port = htons(iSvrPort);
    sa.sin_family = AF_INET;
    int retCode = ::connect(socket,(struct sockaddr*)&sa,sizeof(sa));  
    if(retCode == -1)  
        return CODE_FALID;
    return CODE_SUCCESS;
int MyHttpsUtil::getGetReqData(const std::string& strSvrIp, int iSvrPort, 
    const std::string& strUri, std::string& strReqData)
    char pLine[256] = {0};
	sprintf_s(pLine, "GET %s HTTP/1.1\r\n", strUri.c_str());
    strReqData.append(pLine);
    memset(pLine, 0, sizeof(pLine));
	sprintf_s(pLine, "Host: %s:%d\r\n",strSvrIp.c_str(), iSvrPort);
    strReqData.append(pLine);
    memset(pLine, 0, sizeof(pLine));
    strReqData.append("Accept: */*\r\n");
    strReqData.append("Connection: close\r\n\r\n");
    //strReqData.append("Connection: keep-alive\r\n\r\n");
    return CODE_SUCCESS;
int MyHttpsUtil::getPostReqData(const std::string& strSvrIp, int iSvrPort, 
    const std::string& strUri, const std::string& strBody, std::string& strReqData)
    char pLine[256] = {0};
	sprintf_s(pLine, "POST %s HTTP/1.1\r\n", strUri.c_str());
    strReqData.append(pLine);
    memset(pLine, 0, sizeof(pLine));
	sprintf_s(pLine, "Host: %s:%d\r\n",strSvrIp.c_str(), iSvrPort);
    strReqData.append(pLine);
    memset(pLine, 0, sizeof(pLine));
    strReqData.append("Accept: */*\r\n");
    strReqData.append("Content-Type: application/json; charset=utf-8\r\n");
    memset(pLine, 0, sizeof(pLine));
    sprintf_s(pLine, "Content-Length: %d\r\n", strBody.length());
    strReqData.append("Connection: close\r\n\r\n");
    strReqData.append(strBody);
    return CODE_SUCCESS;
int MyHttpsUtil::readResponseToString(SSL* ssl, std::string& strRespData)
    // 读取响应体数据,一次读1k
    char pBody[1024 + 1] = {0};
    int readSize = sizeof(pBody) -1;
    int readLen = 0;
    while( (readLen = SSL_read(ssl, pBody, readSize)) > 0 )
        strRespData.append(pBody);
        memset(pBody, 0, sizeof(pBody));
    if(readLen < 0)
        int sslErrCode = SSL_get_error(ssl,readLen);
        strRespData = "SSL_read error,openssl errCode = ";
        char errCode[11] = {0};
        sprintf_s(errCode, "%d", sslErrCode);
        strRespData.append(errCode);
        return CODE_FALID;
    strRespData.append(pBody);
    return CODE_SUCCESS;
int MyHttpsUtil::readResponseToFile(SSL* ssl, const std::string& strFilePath, std::string& strErrMsg)
    FILE *fp = fopen(strFilePath.c_str(), "wb+");
    if(fp == NULL)
        strErrMsg = "fopen error,filePath:";
        strErrMsg.append(strFilePath);
        return CODE_FALID;
    char pBody[1024 + 1] = {0};
    int readSize = sizeof(pBody) -1;
    int readLen = 0;
    while( (readLen = SSL_read(ssl, pBody, readSize)) > 0 )
        if( fwrite(pBody, 1, readLen, fp) != readLen)
            strErrMsg = "fwrite error";
            return CODE_FALID;
        memset(pBody, 0, sizeof(pBody));
    if(readLen < 0)
        int sslErrCode = SSL_get_error(ssl,readLen);
        strErrMsg = "SSL_read error,openssl errCode = ";
        char errCode[11] = {0};
        sprintf_s(errCode, "%d", sslErrCode);
        strErrMsg.append(errCode);
        return CODE_FALID;
    if( fwrite(pBody, 1, readLen, fp) != readLen)
        strErrMsg = "fwrite error";
        return CODE_FALID;
    fclose(fp);
    return CODE_SUCCESS;
}

测试调用函数所在cpp文件包含以上类#include "MyHttpsUtil.h",请求处理函数:

bool CXXX::GetHttpInfo_OpenSSL(CString& strIP, CString& strPort)
	//CString strURL= L"http://www.baidu.com/";
	CString strURL= L"https://www.baidu.com/";
	CString strServer, strObject;
	INTERNET_PORT wPort;
	DWORD dwType;
	if (!AfxParseURL(strURL, dwType, strServer, strObject, wPort))
		return false;//URL解析错误
	string strRequestUrl = CStringToString(strURL);
	string strResponse;
	//if (MyHttpsUtil::getInstance()->getRequest(strRequestUrl, 443, "/", strResponse) == 0)
	if (MyHttpsUtil::getInstance()->getRequest(CStringToString(strServer),wPort,strRequestUrl, strResponse) == 0)
                //字符串函数操作处理见https://zh.cppreference.com
		CString strGetSentence = _T("");
		int ss = strResponse.find('{');
		int ee = strResponse.rfind('}');
		if (ee > ss)
			string sub = strResponse.substr(ss, ee - ss + 1);
			strGetSentence = StringToCString(sub);
		strGetSentence = strGetSentence + char(13) + char(10);
		strResponse = CStringToString(strGetSentence);
		Json::Reader reader;
		Json::Value root;
		if (reader.parse(strResponse, root))  // reader将Json字符串解析到root,root将包含Json里所有子元素  
			string strMessage;
			strMessage = root["field"].asString();
		return false;
	strResponse.clear();//*/
	return true;
}

方法三(curl库+OpenSSL?)

下载库放到工程目录下,工程属性C/C++下“常规”里“附加包含目录”添加"$(ProjectDir)\include;$(ProjectDir)\aes_base64;ws2_32.lib;wldap32.lib;.\code_base;.\json;"等,“链接”里常规添加附加库目录,或者“输入”里添加附加依赖项等。

#pragma comment(lib, "ws2_32.lib")  
#pragma comment(lib, "wldap32.lib")  
#pragma comment(lib, "curllib_lib/libcurl.lib")
// http GET
CURLcode GetHttpInfo_curl(const std::string &url, std::string &response)
	// init curl
	CURL *curl = curl_easy_init();
	// res code
	CURLcode res;
	if (curl)
		// set params
		curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // url
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false); // if want to use https
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false); // set peer and host verify false
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, req_reply);
		curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&response);
		curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
		//curl_easy_setopt(curl, CURLOPT_HEADER, 1);
		curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 3); // set transport and time out time
		curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3);
		// start req
		res = curl_easy_perform(curl);
	// release curl
	curl_easy_cleanup(curl);
	return res;
size_t req_reply(void *ptr, size_t size, size_t nmemb, void *stream)
	string *str = (string*)stream;
        /*Json::Reader reader;
	Json::Value root;
	if (reader.parse(*str, root))
		string strMessage;
 
推荐文章