Qt、Curl 异步请求

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013495598/article/details/88941986

curl异步请求相比于同步请求差别不大,配合业务使用时会复杂点;

注意点:

1、使用CURLM模式时,curl_easy_init返回的CURL会复用,一旦请求频率过高会导致返回数据错乱;

2、可在请求之前添加域名解析,加速接口的请求速度,效果非常明显;

3、本示例删除了curl_easy_setopt(curl, CURLOPT_TIMEOUT, TIME_OUT) 超时设置是为了配合业务需要,可自行添加;

4、示例使用了Qt的类型和信号槽

1:创建一个业务线程:轮询curl是否有可用数据

#ifndef CURL_THREAD_H
#define CURL_THREAD_H

#include <QThread>

class CurlServer;
class CurlThread : public QThread
{
	Q_OBJECT
signals :
    //str:返回回调数据的地址
	void signal_CurlThread_SendData(void* str);
public:
	CurlThread(QObject *parent);
	~CurlThread();
	void stop();
	void setToken(const std::string token);
    /*intptr_t:返回回调数据的地址,在业务侧保存该数据对应的请求类型
    * 用来匹配接口返回的数据类型
    */
	intptr_t gets (std::string& strUrl);
	intptr_t posts(std::string& strUrl, std::string& strPara);
protected:
	virtual void run();
private:
	CurlServer * m_pCurlServer = nullptr;
	bool m_bIsStop = false;
};

#endif // CURL_THREAD_H
#include "curl_thread.h"
#include "./curl_server.h"
#pragma execution_character_set("utf-8")
CurlThread::CurlThread(QObject *parent)
	: QThread(parent)
{
	m_pCurlServer = new CurlServer(this);
	//ToDo:连接信号
	connect(m_pCurlServer, &CurlServer::signal_CurlServer_SendData, this, &CurlThread::signal_CurlThread_SendData);
}

CurlThread::~CurlThread()
{
	m_bIsStop = true;
}

void CurlThread::stop()
{
	m_bIsStop = true;
}

void CurlThread::setToken(const std::string token)
{
	m_pCurlServer->setToken(token);
}

intptr_t CurlThread::gets(std::string& strUrl)
{
	return m_pCurlServer->gets(strUrl, "");
}

intptr_t CurlThread::posts(std::string & strUrl, std::string & strPara)
{
	return m_pCurlServer->posts(strUrl, strPara);
}

void CurlThread::run()
{
	while (true)
	{
		if (m_bIsStop)
			break;
		m_pCurlServer->curlMultiHandle();
		Sleep(50);
	}
}

2、curl接口处理

#ifndef CURL_SERVER_H
#define CURL_SERVER_H

#include <QObject>
#include <QMutex>
#include <QMap>
#include <QWaitCondition>
#include <QReadWriteLock>
#include "curl/curl.h"

class CurlServer : public QObject
{
	Q_OBJECT
signals:
	void signal_CurlServer_SendData(void* str, intptr_t curl);
public:
	CurlServer(QObject *parent);
	~CurlServer();
	inline void setToken(const std::string token) { m_sToken = token;};
	intptr_t gets(std::string& strUrl, const std::string& sProxy, char* szHost = nullptr, const char * pCaPath = nullptr);

	intptr_t posts(std::string strUrl, const std::string strPara, char* szHost = nullptr, const char * pCaPath = nullptr);

	void curlMultiHandle();
private:

	CURLM *m_pMultiHandle = nullptr;//重复处理的句柄
	QMutex m_mutex1;//对map读写加锁
	QMutex m_mutex2;//配合QWaitCondition防止线程消耗CPU
	QWaitCondition m_waitCondition;//等待

	/*保存回调数据的地址和CURL的值
    * 用来匹配CURL对应的接口类型
    */
	QMap<std::string*,intptr_t> m_mapCurlStr;
    /*保存回调数据的地址和post请求Para的地址
    * 防止内存泄漏
    */
	QMap<std::string*, std::string*> m_mapParPtr;

	std::string m_sToken = "";

};

#endif // CURL_SERVER_H

实现

#include "curl_server.h"
#pragma execution_character_set("utf-8")
CurlServer::CurlServer(QObject *parent)
	: QObject(parent)
{
	if (m_pMultiHandle == nullptr)
	{
		CURLM * curl_m = curl_multi_init();
		m_pMultiHandle = curl_m;
	}
	m_mapCurlStr.clear();
	m_mapDomainIp.clear();
}

CurlServer::~CurlServer()
{
	if (m_pMultiHandle)
	{
		curl_multi_cleanup(m_pMultiHandle);
		m_pMultiHandle = nullptr;
	}
}
static size_t OnWriteData(void* buffer, size_t size, size_t nmemb, void* lpVoid)
{
	std::string* str = dynamic_cast<std::string*>((std::string *)lpVoid);
	if (NULL == str || NULL == buffer)
	{
		return -1;
	}
	char* pData = (char*)buffer;
	str->append(pData, size * nmemb);
	return nmemb;
}
intptr_t CurlServer::gets(std::string& strUrl, const std::string& sProxy, char* szHost, const char * pCaPath)
{
	CURL * curl = curl_easy_init();
	if (NULL == curl)
	{
		return -1;
	}
	struct curl_slist *headers = NULL;
	if (NULL != szHost)
		headers = curl_slist_append(headers, szHost);

	if (m_sToken != "") {
		std::string str = "token:" + m_sToken;
		headers = curl_slist_append(headers, str.c_str());
	}
	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, TIME_OUT);

	if (!sProxy.empty())
	{
		curl_easy_setopt(curl, CURLOPT_PROXY, sProxy.c_str());
	}

	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);

	m_mutex1.lock();
	std::string *str = new std::string;
	m_mapCurlStr.insert(str, (intptr_t)curl);
	m_mutex1.unlock();
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, str);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);

	if (NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}
	curl_multi_add_handle(m_pMultiHandle, curl);
	m_waitCondition.wakeAll();	//唤醒线程
	return (intptr_t)str;
}

intptr_t CurlServer::posts(std::string strUrl, const std::string strPara, char * szHost, const char * pCaPath)
{
	std::string host = "";
	std::string orgUrl = strUrl;
	setUrlAndHost(strUrl, &szHost, host);

	std::string *curlPara = new std::string(strPara) ;
	CURL * curl = curl_easy_init();
	if (NULL == curl)
	{
		return -1;
	}
	curl_slist *headers = NULL;
	if (NULL != szHost)
	{
		headers = curl_slist_append(headers, szHost);
	}

	headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
	if (m_sToken != "") {
		std::string str = "token:" + m_sToken;
		headers = curl_slist_append(headers, str.c_str());		
	}

	curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
	curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, TIME_OUT);

	curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);

	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, curlPara->c_str());
	curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(curlPara->c_str()));

	m_mutex1.lock();
	std::string *str = new std::string;
	m_mapCurlStr.insert(str, (intptr_t)curl);
	m_mapParPtr.insert(str, curlPara);
	m_mutex1.unlock();
	
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, str);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
	curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
	curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);

	if (NULL == pCaPath)
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, false);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, false);
	}
	else
	{
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
		curl_easy_setopt(curl, CURLOPT_CAINFO, pCaPath);
	}

	curl_multi_add_handle(m_pMultiHandle, curl);
	m_waitCondition.wakeAll();	//唤醒线程
	return (intptr_t)str;
}

void CurlServer::curlMultiHandle()
{
	if (m_mapCurlStr.count() == 0)
	{
		m_mutex2.lock();
		m_waitCondition.wait(&m_mutex2);
		m_mutex2.unlock();
	}

	int running_handles;
	m_mutex1.lock();
	CURLMcode curlMCode = curl_multi_perform(m_pMultiHandle, &running_handles);
	int      msgs_left;
	CURLMsg* msg = curl_multi_info_read(m_pMultiHandle, &msgs_left);
	if (msg)
	{
		bool hasMsg = false;
		if (CURLMSG_DONE == msg->msg)
		{
			for (auto ite = m_mapCurlStr.begin();ite != m_mapCurlStr.end();)
			{
				if (ite.value() == (intptr_t)msg->easy_handle)
				{
					std::string *stdstr = ite.key();

					auto itep  = m_mapParPtr.find(stdstr);
					if (itep != m_mapParPtr.end())
					{
						std::string *strPara = itep.value();
						if (strPara)
						{
							delete strPara;
							strPara = nullptr;
						}
						m_mapParPtr.erase(itep);
					}

					emit signal_CurlServer_SendData((void*)stdstr);
					ite = m_mapCurlStr.erase(ite);
					curl_multi_remove_handle(m_pMultiHandle, msg->easy_handle);
					curl_easy_cleanup(msg->easy_handle);
					break;
				}
				ite++;
			}
		}
	}
	m_mutex1.unlock();
	return;
}

猜你喜欢

转载自blog.csdn.net/u013495598/article/details/88941986