C++下使用libcurl实现微信公众号推送消息到粉丝账号

最近,需要将公司的产品报警信息推送到用户的微信上,之前有过FaceBook和Twitter API使用经验,但真正应用到产品还没有过,今天跑通了原理,记录一下:


入门原理:http://www.360doc.com/content/17/0429/17/30371403_649617749.shtml


为什么选择libcurl呢?因为,微信等的API都是SSL安全https访问,比较过来,就libcurl最简单方便了!!!




闲话不多说,直接上代码,末尾我附上整个VS2013打包工程:

// 
// 接入微信公众号消息发送API by http://blog.csdn.net/houwenbin1986/
//
// 更多微信公众号API参考:https://mp.weixin.qq.com/debug/
//
//本文档原始来自 http://blog.csdn.net/xulei364132789/article/details/44650757
//最近在写一个应用程序, 需要与HTTP服务器进行数据交互, 于是乎自己写了一个类似wget的功能的客户端, 
//实现很简单, 但是功能不给力, 只可基本功能. 于是又在网上找了找, 发现使用libcurl库很方便, 很强大, 
//比起wget之类的, 强大不是一点点. 
//下面是常用的GET/POST/HTTPS/多线程HTTPS的使用方法. 仅仅是一个实现函数. 


/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
 *
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ***************************************************************************/

/*
	本文件介绍整curl 库常用的使用方法. 
	包括GET/POST/UPLOAD/HTTPS

					by xulei 

				2015年3月24日 00:15:20    
*/
#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>
//#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
//#include <sys/mman.h>
//#include <arpa/inet.h>
#include <errno.h>
//#include <arpa/inet.h>
//#include <semaphore.h> 
#include <stdarg.h>
//#include <pthread.h>
#include <signal.h>
#include <time.h>
//#include <sys/param.h>
//#include <getopt.h>
#include <direct.h>

#include <string>

#include <curl/curl.h>
#include <openssl/crypto.h>


#define curl_printf printf

#define CURL_BUF_MAX_LEN  1024
#define CURL_NAME_MAX_LEN 512
#define CURL_URL_MAX_LEN  1024

enum curl_method
{
	CURL_METHOD_GET  = 1,
	CURL_METHOD_POST = 2,
};

struct curl_http_args_st
{
	int  curl_method;	// curl 方法命令,enum curl_method
	char url[CURL_URL_MAX_LEN];		// URL 
	
	char file_name[CURL_NAME_MAX_LEN];	// 返回数据保存为文件
	FILE *file_fd;						// 文件所指向的描述符, 用完后需要手动fclose

	int  data_len;						// 文件数据保存在内存中的长度
	char *data;							// 文件数据保存在内存中的指针, 用完后手动free 

	char post_data[CURL_BUF_MAX_LEN];	// POST 表单数据
	char post_file[CURL_NAME_MAX_LEN];	// POST 文件名
};

/* we have this global to let the callback get easy access to it */
//static pthread_mutex_t *lockarray;
//
//static void lock_callback(int mode, int type, char *file, int line)
//{
//  (void)file;
//  (void)line;
//  if (mode & CRYPTO_LOCK) {
//    pthread_mutex_lock(&(lockarray[type]));
//  }
//  else {
//    pthread_mutex_unlock(&(lockarray[type]));
//  }
//}
//
//static unsigned long thread_id(void)
//{
//  unsigned long ret;
//
//  ret=(unsigned long)pthread_self();
//  return(ret);
//}
//
//static void init_locks(void)
//{
//  int i;
//
//  lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *
//                                            sizeof(pthread_mutex_t));
//  for (i=0; i<CRYPTO_num_locks(); i++) {
//    pthread_mutex_init(&(lockarray[i]),NULL);
//  }
//
//  CRYPTO_set_id_callback((unsigned long (*)())thread_id);
//  CRYPTO_set_locking_callback((void (*)())lock_callback);
//}
//
//static void kill_locks(void)
//{
//  int i;
//
//  CRYPTO_set_locking_callback(NULL);
//  for (i=0; i<CRYPTO_num_locks(); i++)
//    pthread_mutex_destroy(&(lockarray[i]));
//
//  OPENSSL_free(lockarray);
//}


size_t curl_write_data_cb(void *buffer, size_t size, size_t nmemb, void *stream)
{
	int len = size * nmemb;
	struct curl_http_args_st *args = (struct curl_http_args_st*)stream;	
	
	if (stream)
	{
		if (args->file_name[0])	// 要写文件
		{
			if (!args->file_fd)
			{
				args->file_fd = fopen(args->file_name, "wb");
				if (args->file_fd == NULL)
				{
					curl_printf("%s[%d]: open file[%s] failed!!\n", __FUNCTION__, __LINE__, args->file_name);
					return 0;
				}
			}
			fwrite(buffer, size, nmemb, args->file_fd);
		}
		args->data = (char*)realloc(args->data, args->data_len + len + 1);	// 多分配一个字节, 以保存\0 
		if (!args->data)
		{
			curl_printf("%s[%d]: realloc failed!!\n", __FUNCTION__, __LINE__);
			return 0;
		}
		memcpy(args->data + args->data_len, buffer, len);
		args->data_len += len;
	}
	
	return len;
}

// 创建一个目录,包括其父目录mkdir -p 
int create_dir(const char *sPathName)
{
	char dirname[CURL_NAME_MAX_LEN] = {0};
	int i, len = strlen(sPathName);
	
	//strncpy(dirname, sPathName, sizeof(dirname));

	//len = strlen(dirname);
	//for (i = 1; i < len; i++) {
	//	if (dirname[i] == '/') {
	//		dirname[i] = 0;
	//		if (access(dirname, F_OK) != 0) { // 判断是否存在
	//			if (mkdir(dirname, 0777) == -1) {
	//				perror("mkdir  error");
 //                   curl_printf("mkdir file: dirname=%s\n", dirname);
	//				return -1;
	//			}
	//		}
	//		dirname[i] = '/';
	//	}
	//}

	return 0;
}

/*
	http get func 
*/
int curl_http_get(struct curl_http_args_st *args)
{
	//创建curl对象 
	CURL *curl; 
	CURLcode return_code;
    int ret = -1;

	// 如果要保存为文件, 先建立文件目录
	if (args->file_name)
	    create_dir(args->file_name);
    
	//curl初始化 
	curl = curl_easy_init(); 
	if (!curl)
	{
		curl_printf("%s[%d]: curl easy init failed\n", __FUNCTION__, __LINE__);
		return ret;;
	}

	if (strncmp(args->url, "https://", 8) == 0)
	{
		#if 1	
		// 方法1, 设定为不验证证书和HOST
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
		#else
		// 方法2, 设定一个SSL判别证书, 未测试
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 1L)
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
		curl_easy_setopt(curl,CURLOPT_CAINFO,"ca-cert.pem"); 	// TODO: 设置一个证书文件
		#endif 
	}
		
	curl_easy_setopt(curl,CURLOPT_HEADER,0);	//设置httpheader 解析, 不需要将HTTP头写传入回调函数
	
	curl_easy_setopt(curl, CURLOPT_URL,args->url);	//设置远端地址 

	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);	// TODO: 打开调试信息
	
	curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);	//设置允许302  跳转
	
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data_cb); 	//执行写入文件流操作 的回调函数
	
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, args);	// 设置回调函数的第4 个参数
    
    curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);	//设置为ipv4类型
	
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); 	//设置连接超时,单位s, CURLOPT_CONNECTTIMEOUT_MS 毫秒

	// curl_easy_setopt(curl,CURLOPT_TIMEOUT, 5);			// 整个CURL 执行的时间, 单位秒, CURLOPT_TIMEOUT_MS毫秒
	
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);		//linux多线程情况应注意的设置(防止curl被alarm信号干扰)

	return_code = curl_easy_perform(curl); 
	if (CURLE_OK != return_code)
	{
		curl_printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(return_code));
		ret  = 0;
	}

	if (args->file_fd)		// 若需要再次处理写入的文件, 在此可以直接使用
	{ 
		//关闭文件流
		fclose(args->file_fd); 
	} 
	if (args->data)		// 若要对返回的内容进行处理, 可在此处理
	{
		curl_printf("data_len:%d\n%s\n", args->data_len, args->data);//打印到终端
		free(args->data);
		args->data = NULL;
	}

	curl_easy_cleanup(curl);

	return ret;
}


/*
	http post func 
*/
int curl_http_post(struct curl_http_args_st *args)
{
	//创建curl对象 
	CURL *curl; 
	CURLcode return_code;
	struct curl_httppost *formpost = NULL;	// POST 需要的参数
	struct curl_httppost *lastptr  = NULL;
    int ret = -1;
	int post_type = 1; // POST 可以有三种方法

	// 如果要保存为文件, 先建立文件目录
	if (args->file_name)
	    create_dir(args->file_name);
    
	//curl初始化 
	curl = curl_easy_init(); 
	if (!curl)
	{
		curl_printf("%s[%d]: curl easy init failed\n", __FUNCTION__, __LINE__);
		return ret;;
	}

	if (strncmp(args->url, "https://", 8) == 0)
	{
		#if 1	
		// 方法1, 设定为不验证证书和HOST
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
		#else
		// 方法2, 设定一个SSL判别证书
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
		curl_easy_setopt(curl,CURLOPT_CAINFO,"ca-cert.pem"); 	// TODO: 设置一个证书文件
		#endif 
	}
		
	curl_easy_setopt(curl,CURLOPT_HEADER,0);	//设置httpheader 解析, 不需要将HTTP头写传入回调函数
	
	curl_easy_setopt(curl, CURLOPT_URL,args->url);	//设置远端地址 

	curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);	// TODO: 打开调试信息
	
	curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1);	//设置允许302  跳转
	
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data_cb); 	//执行写入文件流操作 的回调函数
	
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, args);	// 设置回调函数的第4 个参数
    
    curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);	//设备为ipv4类型
	
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30); 	//设置连接超时,单位s, CURLOPT_CONNECTTIMEOUT_MS 毫秒

	// curl_easy_setopt(curl,CURLOPT_TIMEOUT, 5);			// 整个CURL 执行的时间, 单位秒, CURLOPT_TIMEOUT_MS毫秒
	
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);		//linux多线程情况应注意的设置(防止curl被alarm信号干扰)

	if (post_type == 1)
	{
		// 方法1, 普通的POST , application/x-www-form-urlencoded
		curl_easy_setopt(curl,CURLOPT_POST, 1);		// 设置 为POST 方法
		curl_easy_setopt(curl,CURLOPT_POSTFIELDS, args->post_data);		// POST 的数据内容
		curl_easy_setopt(curl,CURLOPT_POSTFIELDSIZE,strlen(args->post_data));	// POST的数据长度, 可以不要此选项
	}
	else if (post_type == 2)
	{
		//方法2, multipart/formdata请求, POST args->post_data 中的数据, 也可以是将文件内容读取到post_data中		
		curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "reqformat", CURLFORM_PTRCONTENTS, "plain", CURLFORM_END);	// 设置POST 参数
		curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "file", CURLFORM_PTRCONTENTS, args->post_data, CURLFORM_CONTENTSLENGTH, strlen(args->post_data), CURLFORM_END);	
		curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
	}
	else if (post_type == 3)
	{
		//添加内容Content-Disposition: form-data; name="reqformat"....plain 
		curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "reqformat", CURLFORM_PTRCONTENTS, "plain", CURLFORM_END);// 设置POST 参数
		// 添加上传文件,  Content-Disposition: form-data; name="file"; filename="1.jpg"; filename为默认的名字, content-type 为默认curl识别的
		//curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "file", CURLFORM_FILE, args->post_file, CURLFORM_END);
		// 添加上传文件,  //Content-Disposition: form-data; name="file"; filename="1.jpg".,   filename为指定的名字, content-type 为默认curl识别的
		//curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "file", CURLFORM_FILE, err_file, CURLFORM_FILENAME, "1.jpg", CURLFORM_END); 
		// 添加上传文件,  //Content-Disposition: form-data; name="file"; filename="1.jpg".,   filename为指定的名字, content-type为指定的类型
		//curl_formadd(&formpost, &lastptr, CURLFORM_PTRNAME, "file", CURLFORM_FILE, err_file, CURLFORM_FILENAME, "1.jpg", CURLFORM_CONTENTTYPE, "image/jpeg", CURLFORM_END);

		// 引用页:  http://blog.csdn.net/zxgfa/article/details/8302059
		curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
	}

	return_code = curl_easy_perform(curl); 
	if (CURLE_OK != return_code)
	{
		curl_printf("curl_easy_perform() failed: %s\n", curl_easy_strerror(return_code));
		ret  = 0;
	}

	if (args->file_fd)		// 若需要再次处理写入的文件, 在此可以直接使用
	{ 
		//关闭文件流
		fclose(args->file_fd); 
	} 
	if (args->data)		// 若要对返回的内容进行处理, 可在此处理
	{
		curl_printf("data_len:%d\n%s\n", args->data_len, args->data);
		free(args->data);
		args->data = NULL;
	}

	curl_easy_cleanup(curl);
	
	if (post_type == 2 || post_type == 3)	// 用这两种方法需要释放POST数据. 
		curl_formfree(formpost);

	return ret;
}

/*
	1, 从参数中传入操作选项.
	2. 若在线程中要用到HTTPS , 请参看allexamples/threaded-ssl.c 文件使用
*/
int main(int argc, char **argv)
{
	//步骤一
	std::string app_id = "";//更改为你的公众号APP_ID
	std::string app_secret = "";//更改为你的公众号APP_SECRET

	std::string access_token = "";//访问Token
	std::string access_token_geturl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + app_id + "&secret=" + app_secret;//获取ACCESS_TOKEN,每天限制2000次,请珍惜!!!
	
	std::string openid = "";//粉丝用户OPENID列表,next_openid:获取关注用户列表偏移量,不填默认从头开始拉取
	std::string openid_geturl = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=" + access_token + "&next_openid=";

	struct curl_http_args_st curl_args;
	memset(&curl_args, 0x00, sizeof(curl_args));

	/* Must initialize libcurl before any threads are started */
	curl_global_init(CURL_GLOBAL_ALL);
	
	/* 多线程使用SSL时, 需要先初始化锁*/
	//init_locks();
  
	#if 0 // GET
	curl_args.curl_method = CURL_METHOD_GET;
	//strncpy(curl_args.url, "http://news.baidu.com/index.html", sizeof(curl_args.url)); // http  test ok 
	//strncpy(curl_args.url, "https://www.baidu.com/index.html", sizeof(curl_args.url)); // https test ok
	strncpy(curl_args.url, access_token_geturl.c_str(), sizeof(curl_args.url)); // https test ok

	strncpy(curl_args.file_name, "index.html", sizeof(curl_args.file_name));
	#endif 

	#if 1 // POST
	curl_args.curl_method = CURL_METHOD_POST;
	std::string mymessagestring = "hello world from PC by curl...";	
	std::string message_sendurl = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + access_token;//客服消息发送接口
	std::string message_detail = "{\"touser\":\"" + openid +"\",\"msgtype\":\"text\",\"text\":{\"content\":\"" + mymessagestring + "\"}}";//发送具体消息
	
	strncpy(curl_args.url, message_sendurl.c_str(), sizeof(curl_args.url));//请求API地址
		
	strncpy(curl_args.post_data, message_detail.c_str(), sizeof(curl_args.post_data)); // 普通POST 1 OK
	// strncpy(curl_args.post_file, "./xx.mp4", sizeof(curl_args.post_file)); // POST 文件OK , 用方法3
	//strncpy(curl_args.post_file, "./post_file.txt", sizeof(curl_args.post_file)); // POST 文件OK

	//strncpy(curl_args.file_name, "index.html", sizeof(curl_args.file_name));
	#endif 

	
	switch(curl_args.curl_method)
	{
		case CURL_METHOD_GET:
		{
			curl_http_get(&curl_args);
			break;
		}
		case CURL_METHOD_POST:
		{
			curl_http_post(&curl_args);
			break;
		}
		default:
		{
			curl_printf("curl method error:%d\n", curl_args.curl_method);
			break;
		}
	}
	
	/*退出时, 释放锁*/
	//kill_locks();

	return 0;
}



完整工程:http://download.csdn.net/download/houwenbin1986/10139811


  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用libcurl库可以让C++程序实现HTTP/HTTPS协议的客户端功能,以下是使用libcurl实现HTTP GET请求的示例代码: ```c++ #include <curl/curl.h> #include <iostream> int main() { CURL* curl; CURLcode res; char errorBuf[CURL_ERROR_SIZE]; std::string response; curl = curl_easy_init(); if (curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://www.example.com"); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuf); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, [](void* ptr, size_t size, size_t nmemb, void* stream) -> size_t { ((std::string*) stream)->append((char*) ptr, size * nmemb); return size * nmemb; }); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); res = curl_easy_perform(curl); if (res != CURLE_OK) { std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl; std::cerr << "Error buffer: " << errorBuf << std::endl; } else { std::cout << "Response: " << response << std::endl; } curl_easy_cleanup(curl); } return 0; } ``` 这个示例代码中,首先调用`curl_easy_init()`函数初始化curl会话,然后设置请求的URL和一些选项,比如是否自动跟随重定向、错误缓冲区等。然后通过设置`CURLOPT_WRITEFUNCTION`选项和回调函数,将响应数据写入response字符串中。最后调用`curl_easy_perform()`函数执行请求操作,如果请求成功,响应数据就会保存在response字符串中。最后调用`curl_easy_cleanup()`函数释放curl会话。 当然,这只是一个简单的示例,实际应用中可能需要更复杂的请求和处理过程,可以通过设置`CURLOPT_POSTFIELDS`选项来发POST请求数据,通过设置`CURLOPT_HTTPHEADER`选项来设置请求头,等等。具体的使用方法可以参考libcurl库的官方文档:https://curl.se/libcurl/c/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值