What I write, what I lose.
Mark下.
主要是使用CURL实现http文件下载,
在此基础上做一个多任务的封装. 附带md5检测.
头文件:
#define HTTPTASKE_OK 0
#define HTTPTASKE_ERROR 1
#define TASKID int
typedef enum
{
STATUS_BASE = 0,
STATUS_IDLE,
STATUS_RUNNING,
STATUS_FINISHED_OK,
STATUS_FINISHED_ERROR,
}HttpTaskStatus_e;
int httptask_init(void);
int httptask_deinit(void);
int httptask_addfiletask(const char* url_s, int timeout_s,
const char* filename_s,
const char* checkmd5sum_s, long checksize_s);
int httptask_addbuffertask(const char* url_s, int timeout_s,
char* buffer_s, long size_buffer_s,
const char* checkmd5sum_s, long checksize_s);
int httptask_run(void);
int httptask_asynrun(void);
int httptask_getitemstatus(int i, HttpTaskStatus_e* status_e, int* percent);
int httptask_getstatus(HttpTaskStatus_e* status_e, int* percent);
实现文件:
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include "common_debug.inc.h"
#include "curl/curl.h"
#include "httptask.h"
#include "md5chk.h"
#define u_strcpy(dest, len, src) strncpy(dest, src, len-1)
#define HTTPTASK_LEN_URL 1024
#define HTTPTASK_LEN_FILENAME 1024
#define HTTPTASK_LEN_MD5SUM 36
#define HTTPTASK_LEN_MD5 20
#define HTTPTASK_NUM_MAXTASK 10
struct _HttpTaskItem
{
//
char url[HTTPTASK_LEN_URL];
int timeout;
char filename[HTTPTASK_LEN_FILENAME];
bool filemode;
char* buffer;
long size_buffer;
//
char check_md5sum[HTTPTASK_LEN_MD5SUM];
long check_size;
//
double dltotal;
double dlnow;
FILE* fp;
long index_buffer;
//md5 count about.
MD5_CTX md5ctx;
unsigned char md5ori[HTTPTASK_LEN_MD5];
size_t dlcount;
//Status about.
HttpTaskStatus_e item_status_e;
double percent;
double base_percent;
};
typedef struct _HttpTaskItem HttpTaskItem;
struct _HttpTaskItemSet
{
int num;
HttpTaskItem httptask[HTTPTASK_NUM_MAXTASK];
HttpTaskStatus_e status_e;
double percent;
};
typedef struct _HttpTaskItemSet HttpTaskItemSet;
HttpTaskItemSet gs_httptaskset = {0};
int httptask_init(void)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &gs_httptaskset;
httptaskset->num = 0;
int i = 0;
for(i=0; i<HTTPTASK_NUM_MAXTASK; i++)
{
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
httptask = httptask;
//httptaskitem_init(httptask);
}
memset(httptaskset, 0, sizeof(HttpTaskItemSet));
httptaskset->status_e = STATUS_IDLE;
return ret;
}
int httptask_deinit(void)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &gs_httptaskset;
httptaskset->num = 0;
int i = 0;
for(i=0; i<HTTPTASK_NUM_MAXTASK; i++)
{
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
httptask = httptask;
//httptaskitem_deinit(httptask);
}
memset(httptaskset, 0, sizeof(HttpTaskItemSet));
httptaskset->status_e = STATUS_IDLE;
return ret;
}
int httptask_addfiletask(const char* url_s, int timeout_s,
const char* filename_s,
const char* checkmd5sum_s, long checksize_s)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &gs_httptaskset;
int index = httptaskset->num;
if(index >= HTTPTASK_NUM_MAXTASK)
{
DBGPRINTF_ERROR("Add File task error.\n");
ret = HTTPTASKE_ERROR;
return ret;
}
(httptaskset->num) ++ ;
HttpTaskItem* httptask = &(httptaskset->httptask[index]);
u_strcpy(httptask->url, HTTPTASK_LEN_URL, url_s);
httptask->timeout = timeout_s;
httptask->filemode = false;
u_strcpy(httptask->filename, HTTPTASK_LEN_FILENAME, filename_s);
httptask->filemode = true;
if(checkmd5sum_s != NULL)
{
u_strcpy(httptask->check_md5sum, HTTPTASK_LEN_MD5SUM, checkmd5sum_s);
}
httptask->check_size = checksize_s;
httptask->item_status_e = STATUS_IDLE;
httptask->percent = -1;
DBGPRINTF_DEBUG("Add File task OK.\n");
return ret;
}
int httptask_addbuffertask(const char* url_s, int timeout_s,
char* buffer_s, long size_buffer_s,
const char* checkmd5sum_s, long checksize_s)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &gs_httptaskset;
int index = httptaskset->num;
if(index >= HTTPTASK_NUM_MAXTASK)
{
DBGPRINTF_ERROR("Add File task error.\n");
ret = HTTPTASKE_ERROR;
return ret;
}
(httptaskset->num) ++ ;
HttpTaskItem* httptask = &(httptaskset->httptask[index]);
u_strcpy(httptask->url, HTTPTASK_LEN_URL, url_s);
httptask->timeout = timeout_s;
httptask->filemode = false;
httptask->buffer = buffer_s;
httptask->size_buffer = size_buffer_s;
if(checkmd5sum_s != NULL)
{
u_strcpy(httptask->check_md5sum, HTTPTASK_LEN_MD5SUM, checkmd5sum_s);
}
httptask->check_size = checksize_s;
httptask->item_status_e = STATUS_IDLE;
httptask->percent = -1;
DBGPRINTF_DEBUG("Add Buffer task OK.\n");
return ret;
}
static int httptask_perform(HttpTaskItemSet* httptaskset);
int httptask_run(void)
{
int ret = HTTPTASKE_OK;
DBGPRINTF_INFO("HttpTask run.\n");
HttpTaskItemSet* httptaskset = &(gs_httptaskset);
ret = httptask_perform(httptaskset);
DBGPRINTF_INFO("HttpTask run finished.\n");
return ret;
}
void* func_httptaskperform(void* arg)
{
HttpTaskItemSet* httptaskset = (HttpTaskItemSet*)arg;
httptask_perform(httptaskset);
return NULL;
}
int httptask_asynrun(void)
{
int ret = HTTPTASKE_OK;
DBGPRINTF_INFO("HttpTask Asyn run.\n");
HttpTaskItemSet* httptaskset = &(gs_httptaskset);
pthread_t pid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
int ret_pthread = pthread_create(&pid, &attr, func_httptaskperform, httptaskset);
if(ret_pthread != 0)
{
DBGPRINTF_ERROR("HttpTask : AsyncRun create pthread error.\n");
ret = HTTPTASKE_ERROR;
}
DBGPRINTF_INFO("HttpTask Asyn run finished.\n");
return ret;
}
int httptask_getitemstatus(int i, HttpTaskStatus_e* status_e, int* percent)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &(gs_httptaskset);
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
*status_e = httptask->item_status_e;
*percent = (httptask->percent>=0.0)?(int)(httptask->percent * 100.0 + 0.5):-1;
return ret;
}
int httptask_getstatus(HttpTaskStatus_e* status_e, int* percent)
{
int ret = HTTPTASKE_OK;
HttpTaskItemSet* httptaskset = &(gs_httptaskset);
*status_e = httptaskset->status_e;
*percent = (httptaskset->percent>=0.0)?(int)(httptaskset->percent * 100.0 + 0.5):-1;
return ret;
}
static size_t HttpWriteContentToFp(char *buffer, size_t size, size_t nitems, void *outstream);
static size_t HttpWriteContentToBuffer(char *buffer, size_t size, size_t nitems, void *outstream);
static int WriteProgress(void* p, double dltotal, double dlnow, double ultotal,double ulnow);
int httptask_countbasepercent(HttpTaskItemSet* httptaskset);
int httptaskitem_addrecvedsize(HttpTaskItem* httptask, size_t size_recv);
int httptaskitem_updatepercent(HttpTaskItem* httptask, double percent);
int httptask_perform(HttpTaskItemSet* httptaskset)
{
int ret = HTTPTASKE_OK;
DBGPRINTF_DEBUG("HttpTask perform.\n");
httptask_countbasepercent(httptaskset);
httptaskset->status_e = STATUS_RUNNING;
int i = 0;
bool finishedok = true;
for(i=0; i<httptaskset->num; i++)
{
DBGPRINTF_DEBUG("HttpTask perform : %d.\n", i+1);
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
httptask->item_status_e = STATUS_RUNNING;
httptask->percent = 0.0;
httptask->dlcount = 0;
//Count md5.
if((httptask->check_md5sum!=NULL) && (strlen(httptask->check_md5sum)>0))
{
MD5Init(&(httptask->md5ctx));
}
//Create CURL handle.
CURLcode ret_curl = CURLE_OK;
CURL* curl = curl_easy_init();
if(curl == NULL)
{
DBGPRINTF_ERROR("curl_easy_init error.\n");
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
break;
}
/*Set url.*/
curl_easy_setopt(curl, CURLOPT_URL, httptask->url);
/*Timeout set.*/
if(httptask->timeout > 0)
{
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, httptask->timeout);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, httptask->timeout);
}
/*Low speed.*/
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 20L);
/*Set debug on.*/
#ifdef HTTPPT_DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#endif
/*Set progress.*/
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, WriteProgress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, httptask);
/*Set callback.*/
//鍖哄垎璁剧疆 鍐機ontent 鍒版枃浠?or 鍒癰uffer.
if(httptask->filemode)
{
FILE* fp = fopen(httptask->filename, "w");
if(fp == NULL)
{
DBGPRINTF_ERROR("Open file to write failed.[%s].\n", httptask->filename);
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
/* always cleanup */
curl_easy_cleanup(curl);
break;
}
httptask->fp = fp;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpWriteContentToFp);
}
else
{
httptask->index_buffer = 0;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HttpWriteContentToBuffer);
}
curl_easy_setopt(curl, CURLOPT_WRITEDATA, httptask);
/* Perform the request, res will get the return code */
ret_curl = curl_easy_perform(curl);
if((httptask->filemode) && (httptask->fp != NULL))
{
fclose(httptask->fp);
httptask->fp = NULL;
}
/* always cleanup */
curl_easy_cleanup(curl);
/* Check return value after curl_easy_perform. */
if(ret_curl != CURLE_OK)
{
DBGPRINTF_ERROR("[CURL]%s.\n", curl_easy_strerror(ret_curl));
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
break;
}
/*Check md5 checksum.*/
if((httptask->check_md5sum!=NULL) && (strlen(httptask->check_md5sum)>0))
{
MD5Final(httptask->md5ori, &(httptask->md5ctx));
char count_md5sum[HTTPTASK_LEN_MD5SUM] = {0};
int i = 0;
char tmp[4] = {0};
for(i=0; i<16; i++)
{
memset(tmp, 0, 4);
snprintf(tmp, 4, "%02x", httptask->md5ori[i]);
memcpy(count_md5sum+i*2, tmp, 2);
}
DBGPRINTF_DEBUG("count_md5sum=%s.\n", count_md5sum);
DBGPRINTF_DEBUG("check_md5sum=%s.\n", httptask->check_md5sum);
if(strcasecmp(httptask->check_md5sum, count_md5sum) == 0)
{
DBGPRINTF_DEBUG("Md5 check OK.\n");
}
else
{
DBGPRINTF_ERROR("Md5 check ERROR.\n");
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
break;
}
}
/*Check downloaded size.*/
if(httptask->check_size!=0)
{
size_t dlcount = (size_t)(httptask->dlcount);
size_t check_size = (size_t)(httptask->check_size);
size_t dlnow = (size_t)(httptask->dlnow);
size_t dltotal = (size_t)(httptask->dltotal);
if( (dltotal != 0) &&
!( (dlcount==check_size) && (dlnow==dltotal) && (dlnow==dlcount) ) )
{
DBGPRINTF_ERROR("Size check ERROR., [checksize:%ld, dlcount:%u, dlnow:%lf, dltotal:%lf].\n",
httptask->check_size, httptask->dlcount, httptask->dlnow, httptask->dltotal);
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
break;
}
if( (httptask->dltotal == 0) &&
!( (dlcount==check_size) && (dlnow==dlcount) ) )
{
DBGPRINTF_ERROR("Size check ERROR., [checksize:%ld, dlcount:%u, dlnow:%lf, dltotal:%lf].\n",
httptask->check_size, httptask->dlcount, httptask->dlnow, httptask->dltotal);
httptask->item_status_e = STATUS_FINISHED_ERROR;
finishedok = false;
break;
}
}
httptask->percent = 1;
}
httptaskset->status_e = finishedok?STATUS_FINISHED_OK:STATUS_FINISHED_ERROR;
return ret;
}
size_t HttpWriteContentToFp(char *buffer, size_t size, size_t nitems, void *outstream)
{
HttpTaskItem* httptask = (HttpTaskItem*)outstream;
//DBGPRINTF_DEBUG("Receive data : %u .\n", nitems);
FILE* fp = httptask->fp;
size_t size_recv = fwrite(buffer, size, nitems, fp);
httptask->dlcount += size_recv;
httptaskitem_addrecvedsize(httptask, size_recv);
//Count md5.
if((httptask->check_md5sum!=NULL) && (strlen(httptask->check_md5sum)>0) && (size_recv > 0))
{
MD5Update(&(httptask->md5ctx), (unsigned char*)buffer, size_recv);
}
return size_recv;
}
size_t HttpWriteContentToBuffer(char *buffer, size_t size, size_t nitems, void *outstream)
{
HttpTaskItem* httptask = (HttpTaskItem*)outstream;
//DBGPRINTF_DEBUG("Receive data : %u .\n", nitems);
long* index = &(httptask->index_buffer);
size_t size_recv = 0;
if((long)(size*nitems) <= httptask->size_buffer - *index)
{
size_recv = size*nitems;
memcpy(httptask->buffer + *index, buffer, size*nitems);
*index += size*nitems;
}
else
{
DBGPRINTF_ERROR("Buffer not enought to store downloaded content.\n");
size_recv = 0 ;
}
httptask->dlcount += size_recv;
httptaskitem_addrecvedsize(httptask, size_recv);
//Count md5.
if((httptask->check_md5sum!=NULL) && (strlen(httptask->check_md5sum)>0) && (size_recv > 0))
{
MD5Update(&(httptask->md5ctx), (unsigned char*)buffer, size_recv);
}
return size_recv;
}
int WriteProgress(void* p, double dltotal, double dlnow, double ultotal,double ulnow)
{
HttpTaskItem* httptask = (HttpTaskItem*)p;
//DBGPRINTF_DEBUG("WriteProgress : %lf, %lf .\n", dlnow, dltotal);
httptask->dltotal = dltotal;
httptask->dlnow = dlnow;
if((httptask->dlnow > 0) && (httptask->dlnow != -1))
{
//double pre_percent = httptask->percent;
httptask->percent = httptask->dlnow / httptask->dltotal;
//double add_percent = httptask->percent - pre_percent;
httptaskitem_updatepercent(httptask, httptask->percent);
}
return 0;
}
int httptaskitem_addrecvedsize(HttpTaskItem* httptask, size_t size_recv)
{
int ret = HTTPTASKE_OK;
return ret;
}
int httptaskitem_updatepercent(HttpTaskItem* httptask, double percent)
{
int ret = HTTPTASKE_OK;
percent = percent;
HttpTaskItemSet* httptaskset = &gs_httptaskset;
httptaskset->percent = 0.0;
int i = 0;
for(i=0; i<httptaskset->num; i++)
{
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
if(httptask->percent>=0.0)
{
httptaskset->percent += httptask->percent * httptask->base_percent ;
}
}
return ret;
}
int httptask_countbasepercent(HttpTaskItemSet* httptaskset)
{
int ret = HTTPTASKE_OK;
int i = 0;
bool sizeset = true;
long totalsize = 0;
for(i=0; i<httptaskset->num; i++)
{
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
if(httptask->check_size <= 0)
{
sizeset = false;
break;
}
totalsize += httptask->check_size;
}
for(i=0; i<httptaskset->num; i++)
{
HttpTaskItem* httptask = &(httptaskset->httptask[i]);
httptask->percent = 0;
if(sizeset)
{
httptask->base_percent = double(httptask->check_size) / double(totalsize);
}
else
{
httptask->base_percent = 1 / httptaskset->num;
}
}
return ret;
}