c++ 实现http或者https通信

概念

http通信的优点很多,当然也有局限性。一般我们开发使用restful形式提供接口,这样可以完成RPC远程调用。

https = http + ssl
ssl是一种安全的传输层协议。通信前先建立安全通道,之后在传递数据。

libcurl库,一个c写的http库,想要支持ssl,编译时需要加入编译选项。

编译库

编译windows版的dll  链接
linux版本的library :编译时加入 ./configure --with-ssl 即可
windows dll已经编译好的库 https://download.csdn.net/download/u013919153/10848458 可下载

编程介绍

简单同步编程 (easy mode)
使用流程(套路)
1.       调用curl_global_init()初始化libcurl
2.       调用curl_easy_init()函数得到 easy interface型指针
3.       调用curl_easy_setopt()设置传输选项
4.       根据curl_easy_setopt()设置的传输选项,实现回调函数以完成用户特定任务
5.       调用curl_easy_perform()函数完成传输任务,返回码
6.       调用curl_easy_cleanup()释放内存
7.       调用curl_global_cleanup() (可以不用调用)
在整过过程中设置curl_easy_setopt()参数是最关键的,了解相关参数及对应作用很重要

重要函数介绍
1.CURLcode curl_global_init(long flags);

描述:
这个函数只能用一次。(其实在调用curl_global_cleanup 函数后仍然可再用)
如果这个函数在curl_easy_init函数调用时还没调用,它讲由libcurl库自动调用,所以多线程下最好主动调用该函数以防止在线程中curl_easy_init时多次调用

注意:虽然libcurl是线程安全的,但curl_global_init是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。
参数:flags
CURL_GLOBAL_ALL                      //初始化所有的可能的调用。(常用)
CURL_GLOBAL_SSL                      //初始化支持 安全套接字层。
CURL_GLOBAL_WIN32            //初始化win32套接字库。
CURL_GLOBAL_NOTHING         //没有额外的初始化。

2.void curl_global_cleanup(void);
描述:
在结束libcurl使用的时候,用来对curl_global_init做的工作清理。类似于close的函数。

注意:虽然libcurl是线程安全的,但curl_global_cleanup是不能保证线程安全的,所以不要在每个线程中都调用curl_global_init,应该将该函数的调用放在主线程中。

3.char *curl_version( );
描述: 打印当前libcurl库的版本。

4.CURL *curl_easy_init( );
描述:
curl_easy_init用来初始化一个CURL的指针(有些像返回FILE类型的指针一样). 相应的在调用结束时要用curl_easy_cleanup函数清理. 一般curl_easy_init意味着一个会话的开始. 它会返回一个easy_handle(CURL*对象), 一般都用在easy系列的函数中.

5.void curl_easy_cleanup(CURL *handle);
描述:
这个调用用来结束一个会话.与curl_easy_init配合着用
参数:
CURL类型的指针.

6.CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
描述:
这个函数最重要了.几乎所有的curl 程序都要频繁的使用它.它告诉curl库.程序将有如何的行为. 比如要查看一个网页的html代码等.(这个函数有些像ioctl函数)
参数:
1 CURL类型的指针
2 各种CURLoption类型的选项.(都在curl.h库里有定义,https://curl.haxx.se/libcurl/c/curl_easy_setopt.html)
3 parameter 这个参数 既可以是个函数的指针,也可以是某个对象的指针,也可以是个long型的变量.它用什么这取决于第二个参数.
CURLoption 这个参数的取值很多.具体的可以查看man手册.

7.CURLcode curl_easy_perform(CURL *handle);
描述:
这个函数在初始化CURL类型的指针 以及curl_easy_setopt完成后调用. 就像字面的意思所说perform就像是个舞台.让我们设置的option 运作起来.
参数:
CURL类型的指针.

8.消息头设置

struct curl_slist *headers=NULL;         /* init to NULL is important */
headers = curl_slist_append(headers, "Hey-server-hey: how are you?");
headers = curl_slist_append(headers, "X-silly-content: yes");

/* pass our list of custom made headers */
curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, headers);

curl_easy_perform(easyhandle);            /* transfer http */

curl_slist_free_all(headers);             /* free the header list */

如果修改已经存在的消息头,直接设置即可;如果删除消息头信息,直接设置对应内容为空即可。

发送http消息后,服务器会返回消息头,如果只是查看消息头内容,使用如下函数
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, 打印函数)
如果想获取特定信息,使用如下方法:
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... );
最后一个参数是接收变量。具体详情查看官网API

下面是完整通信设置代码
 

//http回调写函数
static size_t   CurlWriteBuffer(char *buffer, size_t size, size_t nmemb, std::string* stream)
{
    //第二个参数为每个数据的大小,第三个为数据个数,最后一个为接收变量
	size_t sizes = size*nmemb;
	if(stream == NULL) 
		return 0;
	stream->append(buffer,sizes);
	return sizes;
}

//http发送封装
int  CPrinterDlg::posturl(std::string& msg,	std::string& url, bool IsSSL)
{
	CURL* pCurl=NULL;        //一个libcurl的handle
	CURLcode res;            //返回状态码
	std::string response;    //返回信息
    
    curl_global_init(CURL_GLOBAL_ALL);      //全局初始化
	pCurl = curl_easy_init();                //创建一个handle

	//设置请求头
	struct  curl_slist* header_ = NULL;
	header_ = curl_slist_append(header_,"Content-Type: application/json;charset=utf-8");
    
    //添加请求头到handle
	curl_easy_setopt(pCurl, CURLOPT_HTTPHEADER, header_);
    
    //设置URL
	curl_easy_setopt(pCurl, CURLOPT_URL, url.c_str());  

	////CURLOPT_WRITEFUNCTION 将后继的动作交给write_data函数处理
	curl_easy_setopt(pCurl,CURLOPT_POSTFIELDS,msg.c_str());            //post请求消息数据    
	curl_easy_setopt(pCurl,CURLOPT_POSTFIELDSIZE,msg.length());        //消息长度
	curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, CurlWriteBuffer);   //回调函数
	curl_easy_setopt(pCurl,CURLOPT_WRITEDATA,&response);               //数据接收变量
	curl_easy_setopt(pCurl,CURLOPT_TIMEOUT,m_settinginfo.m_http_timeout);  //连接超时时间

    //不支持ssl验证
	if(m_settinginfo.m_ssl == 0)
	{
		curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0);//设定为不验证证书和HOST
		curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0);
	}
	else
	{
		// 配置 https 请求所需证书
		if (m_settinginfo.m_ssl == 1)    //ssl单向验证,不验证服务器
		{
			curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 0L);		
		}else
		{//双向验证
			curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, 1L);
			curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, 0);
			curl_easy_setopt(pCurl,CURLOPT_CAINFO,ca_info.ca_path.c_str()); 
		}

        //设置客户端信息
		curl_easy_setopt(pCurl, CURLOPT_SSLCERT, ca_info.client_cert_path.c_str());
		curl_easy_setopt(pCurl,CURLOPT_SSLCERTTYPE,"PEM");  
		curl_easy_setopt(pCurl, CURLOPT_SSLKEY, ca_info.client_key_path.c_str());
		curl_easy_setopt(pCurl,CURLOPT_SSLKEYTYPE,"PEM"); 
        
        //如果客户端证书密钥使用密码加密,设置加密密码
		//curl_easy_setopt(pCurl, CURLOPT_KEYPASSWD, "your_key_password");
	}
    
    //执行http连接
	res = curl_easy_perform(pCurl);

    //清除消息头
	curl_slist_free_all(header_);
	
    //清除handle
    curl_easy_cleanup(pCurl);

	return 0;
}

 

更多信息查看网址:https://curl.haxx.se/libcurl/c/  libcurl库官网
参考网址:
https://www.cnblogs.com/lifan3a/articles/7479256.html
https://www.cnblogs.com/yongpan/p/8084854.html
https://www.cnblogs.com/cposture/p/9029014.html#_lab2_2_0

你可能感兴趣的:(c++)