版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}