欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

解决curl内存泄露的问题

程序员文章站 2022-05-07 15:17:38
...

 

情景是一个程序一直执行Post,通过http协议上传数据。

一般的办法是:

先curl_easy_init();

之后再curl_easy_perform(curl);

最后 curl_easy_cleanup(curl); 

但是这种方法是存在内存泄露的。参见https://*.com/questions/11494950/memory-leak-from-curl-library

示例:

#include <iostream>
#include "curl/curl.h"
int main(void)
{
    CURL *curl;
    CURLcode res;

    curl = curl_easy_init();
    if(curl) {
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.del.icio.us/dt");
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, true);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2);
        curl_easy_setopt(curl, CURLOPT_CAINFO, "C:\\Users\\bryan\\GeoTrustGlobalCA.crt");
        /* Perform the request, res will get the return code */ 
        res = curl_easy_perform(curl);
        /* Check for errors */ 
        if(res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));

        /* always cleanup */ 
        curl_easy_cleanup(curl);
    }
    return 0;
}

解决办法是执行curl_global_cleanup()

但是考虑到如果使用http长连接的话,可能会效率更高,因此,在执行上传部分添加了时间判断逻辑,如果超过1分钟就执行curl_global_cleanup(),然后重新新建一个curl对象,否则就使用原来的curl对象。

经过测试,执行curl_global_cleanup()后是没有内存泄露的。

#ifndef HTTPCLIENT_HPP
#define HTTPCLIENT_HPP

#include <chrono>

#define CURL_STATICLIB   //静态链接
#include <curl/curl.h>
#include <qdebug.h>

class HttpClient
{

private:  // 不允许复制
     HttpClient( const HttpClient& );
     const HttpClient& operator=( const HttpClient& );

public:
    HttpClient()
    {
        curl_global_init(CURL_GLOBAL_WIN32);
        m_curl = curl_easy_init();//init()
		m_oldTimePoint = std::chrono::system_clock::now();
    }

    ~HttpClient()
    {
        curl_easy_cleanup(m_curl);
        curl_global_cleanup();
    }

    bool send(const std::string &strUrl,std::string &jsonStr)
    {
        string response;
        //string url = "http://127.0.0.1:8080/";


		std::chrono::system_clock::time_point nowTimePoint = std::chrono::system_clock::now();

		std::chrono::duration<double, std::milli> tm = nowTimePoint - m_oldTimePoint;	// 毫秒
		
		if (tm.count() > 60 * 1000)//如果超过60秒就释放资源,重新生成url对象
		{
			curl_easy_cleanup(m_curl);
			curl_global_cleanup();
			m_curl = curl_easy_init();
			m_oldTimePoint = nowTimePoint;
		}


        int httpCode = httpPost(strUrl, jsonStr, response);
        qDebug() << "retCode:" << httpCode;

        if (httpCode == CURLE_OK)//CURLE_OK=0
            return true;
        else
            return false;
    }

private:

    CURL* m_curl;
	std::chrono::system_clock::time_point m_oldTimePoint;

    int httpPost(const std::string & strUrl, const std::string & strPost, std::string & strResponse)
    {
        CURLcode res;
        CURL* curl = m_curl;// curl_easy_init();
        if (NULL == curl)
        {
            curl = curl_easy_init();
           // return CURLE_FAILED_INIT;
        }

        if ( false )//用于调试的设置
        {
            curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
            curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, OnDebug);
        }

        curl_easy_setopt(curl, CURLOPT_URL, strUrl.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1);//设置为非0表示本次操作为POST
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, strPost.c_str());
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, OnWriteData);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&strResponse);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, 3000);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT, 300);

        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);  //支持服务器跳转
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L);  // enable TCP keep-alive for this transfer
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPIDLE, 120L);	// keep-alive idle time to 120 seconds
        curl_easy_setopt(curl, CURLOPT_TCP_KEEPINTVL, 60L);	// interval time between keep-alive probes: 60 seconds

        struct curl_slist* headers = NULL;
        headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);

        res = curl_easy_perform(curl);

        curl_slist_free_all(headers);//清理headers,防止内存泄漏
        //curl_easy_cleanup(curl);
        return res;
    }

    static int OnDebug(CURL *, curl_infotype itype, char * pData, size_t size, void *)//curl调试
    {
        if (itype == CURLINFO_TEXT)
        {
            qDebug() << "[TEXT]"<< pData;//logOutput(string(pData));//printf("[TEXT]%s\n", pData);
        }
        else if (itype == CURLINFO_HEADER_IN)
        {
            qDebug() << "[HEADER_IN]" << pData; // printf("[HEADER_IN]%s\n", pData);
        }
        else if (itype == CURLINFO_HEADER_OUT)
        {
            qDebug() << "[HEADER_OUT]" << pData; //printf("[HEADER_OUT]%s\n", pData);
        }
        else if (itype == CURLINFO_DATA_IN)
        {
            qDebug() << "[DATA_IN]" << pData; //printf("[DATA_IN]%s\n", pData);
        }
        else if (itype == CURLINFO_DATA_OUT)
        {
            qDebug() << "[DATA_OUT]" << pData; //printf("[DATA_OUT]%s\n", pData);
        }
        return 0;
    }

    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;
    }

};


#endif // HTTPCLIENT_HPP

 

相关标签: curl