DownloadThread.h
#ifndef DOWNLOAD_THREAD_H
#define DOWNLOAD_THREAD_H
#include <functional>
#include <mutex>
#include <list>
#include <thread>
#include <atomic>
#include <condition_variable>
#include <curl/curl.h>
typedef struct _Task_
{
int nIndex; // 图片index
std::string strUrl; // url: http or https
std::string strDescription; // 用于标识不同的图片(ID),透传到Callback
std::string strData; // 下载得到的数据
int nErrorCode; // 错误码
std::string strErrorMsg; // 错误信息
} Task;
using DownloadThreadCallback = std::function<void(const Task&)>;
class DownloadThread
{
public:
DownloadThread() = default;
~DownloadThread()=default;
int Initialize(const DownloadThreadCallback &cb);
int AddDownloadTask(const Task &task);
void Finish();
private:
int InitializeCURL();
void FinishCURL();
void Run();
void CallBack(const Task &t);
private:
int m_threadId;
DownloadThreadCallback m_callback;
CURL *m_curl_handle;
std::thread *m_thread;
std::atomic<bool> m_bIsRunning;
std::list<Task> m_taskList;
std::mutex m_mutexTaskList;
std::condition_variable m_condition;
};
#endif // DOWNLOAD_THREAD_H
DownloadThread.cpp
#include "DownloadThread.h"
#include "string.h"
#include <iostream>
#include <string>
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
char* memory = (char*)malloc(realsize);
if (memory == NULL) {
/* out of memory! */
printf("not enough memory (realloc returned NULL)\n");
return 0;
}
memcpy(memory, contents, realsize);
Task *t = (Task*)userp;
t->strData.append(memory, realsize);
free(memory);
return realsize;
}
int DownloadThread::Initialize(const DownloadThreadCallback &cb)
{
m_callback = cb;
if (InitializeCURL() != 0)
{
return -1;
}
m_bIsRunning = true;
m_thread = new std::thread(&DownloadThread::Run, this);
}
void DownloadThread::Finish()
{
m_bIsRunning = false;
if (m_thread->joinable())
{
m_thread->join();
}
FinishCURL();
}
void DownloadThread::FinishCURL()
{
/* cleanup curl stuff */
curl_easy_cleanup(m_curl_handle);
/* we're done with libcurl, so clean it up */
curl_global_cleanup();
}
int DownloadThread::AddDownloadTask(const Task &task)
{
std::lock_guard<std::mutex> lk(m_mutexTaskList);
m_taskList.push_back(task);
m_condition.notify_one();
return 0;
}
void DownloadThread::Run()
{
while (m_bIsRunning.load())
{
//std::cout << "thread is running" << std::endl;
std::unique_lock<std::mutex> lk(m_mutexTaskList);
while (m_taskList.empty())
{
m_condition.wait(lk);
}
// get first element
Task task;
{
//std::lock_guard<std::mutex> lkg(m_mutexTaskList);
task = m_taskList.front();
m_taskList.pop_front();
}
//std::cout << "get task url: " << task.strUrl << std::endl;
CURLcode res;
curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, (void *)&task);
/* specify URL to get */
curl_easy_setopt(m_curl_handle, CURLOPT_URL, const_cast<char*>(task.strUrl.c_str()));
//curl_easy_setopt(m_curl_handle, CURLOPT_URL, "http://10.66.91.15:7777/ld/smog/2612_src.jpg");
/* get it! */
res = curl_easy_perform(m_curl_handle);
/* check for errors */
if (res != CURLE_OK) {
// fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
task.nErrorCode = res;
task.strErrorMsg = curl_easy_strerror(res);
}
else {
/*
* Now, our chunk.memory points to a memory block that is chunk.size
* bytes big and contains the remote file.
*
* Do something nice with it!
*/
/*printf("%lu bytes retrieved\n", task.strData.size());*/
long lStateCode = 0;
res = curl_easy_getinfo(m_curl_handle, CURLINFO_RESPONSE_CODE, &lStateCode);
if (res != CURLE_OK || lStateCode != 200)
{
task.nErrorCode = lStateCode;
// task.strErrorMsg = curl_easy_strerror(res);
task.strErrorMsg = "http state code is " + std::to_string(lStateCode);
}
}
// callback
CallBack(task);
}
}
int DownloadThread::InitializeCURL()
{
CURLcode error;
curl_global_init(CURL_GLOBAL_ALL);
/* init the curl session */
m_curl_handle = curl_easy_init();
///* specify URL to get */
////curl_easy_setopt(curl_handle, CURLOPT_URL, "http://10.66.91.15:7777/ld/smog/2612_src.jpg");
/* send all data to this function */
curl_easy_setopt(m_curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
/* we pass our 'chunk' struct to the callback function */
/*curl_easy_setopt(m_curl_handle, CURLOPT_WRITEDATA, (void *)this);*/
curl_easy_setopt(m_curl_handle, CURLOPT_FOLLOWLOCATION, 1);
/* some servers don't like requests that are made without a user-agent
field, so we provide one */
//curl_easy_setopt(m_curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
// https, skip the verification of the server's certificate.
curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(m_curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
/* 设置连接超时,单位:毫秒 */
curl_easy_setopt(m_curl_handle, CURLOPT_CONNECTTIMEOUT_MS, 1000L);
// add by yexiaoyogn 10 second time out
curl_easy_setopt(m_curl_handle, CURLOPT_TIMEOUT_MS, 2000);
//add yexiaoyong set time out
curl_easy_setopt(m_curl_handle, CURLOPT_NOSIGNAL, 3);
}
void DownloadThread::CallBack(const Task &t)
{
m_callback(t);
}
demo.cpp
#include "DownloadThread.h"
#include <iostream>
#include <thread>
#include <sstream>
#include <fstream>
#include <string>
void TaskCallBack(const Task &t)
{
std::ostringstream str;
str << "callback ...." << std::endl;
str << "task id is " << t.nIndex << " url is " << t.strUrl << " data size is " << t.strData.size() << std::endl;
str << "error number is " << t.nErrorCode << " error msg is " << t.strErrorMsg << std::endl;
std::cout << str.str() << std::endl;
if (t.nErrorCode == 0)
{
std::string filename = t.strDescription + ".jpg";
std::fstream file;
file.open(filename, std::fstream::in | std::fstream::out | std::fstream::binary | std::fstream::app);
file.write(t.strData.c_str(), t.strData.size());
file.close();
}
}
int main()
{
DownloadThread dt;
dt.Initialize(TaskCallBack);
Task t;
int cnt = 0;
while (true)
{
t.nIndex = cnt++;
t.strDescription = "task_"+std::to_string(cnt);
t.strUrl = "http://10.66.91.15:7777/ld/smog/2612_src.jpg";
dt.AddDownloadTask(t);
t.nIndex = cnt++;
t.strDescription = "task_" + std::to_string(cnt);
t.strUrl = "http://www.baidu.com";
dt.AddDownloadTask(t);
t.nIndex = cnt++;
t.strDescription = "task_" + std::to_string(cnt);
t.strUrl = "http://10.66.91.15:7777/ld/smog/2612_srcxzz.jpg";
dt.AddDownloadTask(t);
}
while (true){
std::this_thread::sleep_for(std::chrono::seconds(1));
}
dt.Finish();
}