使用CURL实现http文件下载加一个多任务的封装.
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; }