介紹一個(gè)C語言實(shí)現(xiàn)的http下載器_第1頁
介紹一個(gè)C語言實(shí)現(xiàn)的http下載器_第2頁
介紹一個(gè)C語言實(shí)現(xiàn)的http下載器_第3頁
介紹一個(gè)C語言實(shí)現(xiàn)的http下載器_第4頁
介紹一個(gè)C語言實(shí)現(xiàn)的http下載器_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

Word介紹一個(gè)C語言實(shí)現(xiàn)的http下載器

做OTA升級(jí)功能時(shí),能直接拿到的往往只是升級(jí)包的鏈接,需要我們自己去(下載),這時(shí)候就需要用到http下載器,下文介紹一個(gè)(C語言)實(shí)現(xiàn)的http下載器。

功能

1、支持chunked方式傳輸?shù)南螺d2、被重定向時(shí)能下載重定向頁面3、要實(shí)現(xiàn)的接口為inthttp_downlo(ad)(char*url,char*save_path)

思路

1、解析輸入的URL,分離出主機(jī),端口號(hào),文件路徑的信息2、解析主機(jī)的DNS3、填充http請(qǐng)求的頭部,給服務(wù)器發(fā)包4、解析收到的http頭,提取狀態(tài)碼,Con(te)nt-length,Transfer-Encoding等字段信息(1)如果是普通的頭則進(jìn)行接下來的正常收包流程(2)如果狀態(tài)碼為302,則從頭里提取出重定向地址,用新的地址重新開始下載動(dòng)作(3)如果傳送方式是chunked的,則進(jìn)行分段讀取數(shù)據(jù)并拼接(4)如果是404或其他狀態(tài)碼則打印錯(cuò)誤信息

缺陷

太多錯(cuò)誤處理,讓代碼看起來不太舒服

其他

1、如何移植到?jīng)]有文件系統(tǒng)的系統(tǒng)中?修改sava_data接口里面的保存就好了2、如何提高下載速度?

增大讀寫buffer緩沖區(qū)

改為多線程,使用Range字段分段讀取,最后再拼在一起

代碼

/************************************************************Copyright(C),2021,Leon,AllRightsReserved.FileName:download.ccoding:UTF-8Descrip(ti)on:實(shí)現(xiàn)簡單的http下載功能Author:LeonVersion:1.0Date:2021-12-21032Function:History:Leon************************************************************/#include#include#include#include#include#include#include#include#include#include#defineHOST_NAME_LEN256#defineURI_MAX_LEN2048#defineRECV_BUF8192#defineRCV_SND_TIMEOUT(10*1000)//收發(fā)數(shù)據(jù)超時(shí)時(shí)間(ms)typedefstruct{intsock;//與服務(wù)器(通信)的socketFILE*in;//sock描述符轉(zhuǎn)為文件指針,方便讀寫charhost_name[HOST_NAME_LEN];//主機(jī)名intport;//主機(jī)端口號(hào)charuri[URI_MAX_LEN];//資源路徑charbuffer[RECV_BUF];//讀寫緩沖intstatus_code;//http狀態(tài)碼intchunked_flag;//chunked傳輸?shù)臉?biāo)志位intlen;//Content-length里的長度charlocation[URI_MAX_LEN];//重定向地址char*save_path;//保存內(nèi)容的路徑指針FILE*save_file;//保存內(nèi)容的文件指針intrecv_data_len;//收到數(shù)據(jù)的總長度time_tstart_recv_time;//開始接受數(shù)據(jù)的時(shí)間time_tend_recv_time;//結(jié)束接受數(shù)據(jù)的時(shí)間}http_t;/*打印宏*/#defineMSG_DEBUG0x01#defineMSG_INFO0x02#defineMSG_ERROR0x04staticintprint_level=/*MSG_DEBUG|*/MSG_INFO|MSG_ERROR;#definelprintf(level,format,argv...)do{if(level}while(0)#defineMIN(x,y)((x)>(y)?(y):(x))#defineHTTP_OK200#defineHTTP_REDIRECT302#defineHTTP_NOT_FOUND404/*不區(qū)分大小寫的st(rs)tr*/char*strncasestr(char*str,char*sub){if(!str||!sub)returnNULL;intlen=strlen(sub);if(len==0){returnNULL;}while(*str){if(strncasecmp(str,sub,len)==0){returnstr;}++str;}returnNULL;}/*解析URL,成功返回0,失敗返回-1*//*:8080/testfile*/intparser_URL(char*url,http_t*info){char*tmp=url,*start=NULL,*end=NULL;intlen=0;/*跳過http://*/if(strncasestr(tmp,"http://")){tmp+=strlen("http://");}start=tmp;if(!(tmp=strchr(start,'/'))){lprintf(MSG_ERROR,"urlinv(ai)ld");return-1;}end=tmp;/*解析端口號(hào)和主機(jī)*/info->port=80;//先附默認(rèn)值80len=MIN(end-start,HOST_NAME_LEN-1);strncpy(info->host_name,start,len);info->host_name[len]='?';if((tmp=strchr(start,':'))if(info->portport>=65535){lprintf(MSG_ERROR,"urlportinvaild");return-1;}/*覆蓋之前的賦值*/len=MIN(tmp-start,HOST_NAME_LEN-1);strncpy(info->host_name,start,len);info->host_name[len]='?';}/*復(fù)制uri*/start=end;strncpy(info->uri,start,URI_MAX_LEN-1);lprintf(MSG_INFO,"parseurlokhost:%s,port:%d,uri:%s",info->host_name,info->port,info->uri);return0;}/*dns解析,返回解析到的第一個(gè)地址,失敗返回-1,成功則返回相應(yīng)地址*/unsignedlongdns(char*host_name){structhostent*host;structin_addraddr;char**pp;host=gethostbyname(host_name);if(host==NULL){lprintf(MSG_ERROR,"gethostbyname%sfailed",host_name);return-1;}pp=host->h_addr_list;if(*pp!=NULL){addr.s_addr=*((unsignedint*)*pp);lprintf(MSG_INFO,"%saddressis%s",host_name,inet_ntoa(addr));pp++;returnaddr.s_addr;}return-1;}/*設(shè)置發(fā)送接收超時(shí)*/intset_socket_option(intsock){structtimevaltimeout;timeout.tv_sec=RCV_SND_TIMEOUT/1000;timeout.tv_usec=RCV_SND_TIMEOUT%1000*1000;lprintf(MSG_DEBUG,"%ds%dus",(int)timeout.tv_sec,(int)timeout.tv_usec);//設(shè)置socket為非阻塞//fcntl(sock,F_SETFL,O_NONBLOCK);//以非阻塞的方式,connect需要重新處理//設(shè)置發(fā)送超時(shí)if(-1==setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char*)return-1;}//設(shè)置接送超時(shí)if(-1==setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char*)return-1;}return0;}/*連接到服務(wù)器*/intconnect_server(http_t*info){intsockfd;structsockaddr_inserver;unsignedlongaddr=0;unsignedshortport=info->port;sockfd=socket(AF_INET,SOCK_STREAM,0);if(-1==sockfd){lprintf(MSG_ERROR,"socketcreatefailed");gotofailed;}if(-1==set_socket_option(sockfd)){gotofailed;}if((addr=dns(info->host_name))==-1){lprintf(MSG_ERROR,"GetDnsFailed");gotofailed;}(mems)et(server.sin_family=AF_INET;server.sin_port=htons(port);server.sin_addr.s_addr=addr;if(-1==connect(sockfd,(structsockaddr*)gotofailed;}info->sock=sockfd;return0;failed:if(sockfd!=-1)close(sockfd);return-1;}/*發(fā)送http請(qǐng)求*/intsend_request(http_t*info){intlen;memset(info->buffer,0x0,RECV_BUF);snprintf(info->buffer,RECV_BUF-1,"GET%sHTTP/1.1""Accept:*/*""User-Agent:Mozilla/5.0(compatible;MSIE5.01;WindowsNT5.0)""Host:%s""Connection:Close",info->uri,info->host_name);lprintf(MSG_DEBUG,"request:%s",info->buffer);returnsend(info->sock,info->buffer,strlen(info->buffer),0);}/*解析http頭*/intparse_http_header(http_t*info){char*p=NULL;//解析第一行fgets(info->buffer,RECV_BUF,info->in);p=strchr(info->buffer,'');//簡單檢查http頭第一行是否合法if(!p||!strcasestr(info->buffer,"HTTP")){lprintf(MSG_ERROR,"badhttphead");return-1;}info->status_code=atoi(p+1);lprintf(MSG_DEBUG,"httpstatuscode:%d",info->status_code);//循環(huán)讀取解析http頭while(fgets(info->buffer,RECV_BUF,info->in)){//判斷頭部是否讀完if(!strcmp(info->buffer,"")){return0;/*頭解析正常*/}lprintf(MSG_DEBUG,"%s",info->buffer);//解析長度Content-length:554if(p=strncasestr(info->buffer,"Content-length")){p=strchr(p,':');p+=2;//跳過冒號(hào)和后面的空格info->len=atoi(p);lprintf(MSG_INFO,"Content-length:%d",info->len);}elseif(p=strncasestr(info->buffer,"Transfer-Encoding")){if(strncasestr(info->buffer,"chunked")){info->chunked_flag=1;}else{/*不支持其他編碼的傳送方式*/lprintf(MSG_ERROR,"Notsupport%s",info->buffer);return-1;}lprintf(MSG_INFO,"%s",info->buffer);}elseif(p=strncasestr(info->buffer,"Location")){p=strchr(p,':');p+=2;//跳過冒號(hào)和后面的空格strncpy(info->location,p,URI_MAX_LEN-1);lprintf(MSG_INFO,"Location:%s",info->location);}}lprintf(MSG_ERROR,"badhttphead");return-1;/*頭解析出錯(cuò)*/}/*保存服務(wù)器響應(yīng)的內(nèi)容*/intsave_data(http_t*info,constchar*buf,intlen){inttotal_len=len;intwrite_len=0;//文件沒有打開則先打開if(!info->save_file){info->save_file=fopen(info->save_path,"w");if(!info->save_file){lprintf(MSG_ERROR,"fopen%serror:%m",info->save_path);return-1;}}while(total_len){write_len=fwrite(buf,sizeof(char),len,info->save_file);if(write_lenbuffer,sizeof(char),read_len,info->in);if(rtn_lenin)){if(errno==EINTR)/*(信號(hào))中斷了讀操作*/{;/*不做處理繼續(xù)往下走*/}elseif(errno==EAGAIN||errno==EWOULDBLOCK)/*超時(shí)*/{lprintf(MSG_ERROR,"socketrecvicetimeout:%dms",RCV_SND_TIMEOUT);total_len-=rtn_len;lprintf(MSG_DEBUG,"readlen:%d",rtn_len);break;}else/*其他錯(cuò)誤*/{lprintf(MSG_ERROR,"freaderror:%m");break;}}else/*讀到文件尾*/{lprintf(MSG_ERROR,"socketclosedbypeer");total_len-=rtn_len;lprintf(MSG_DEBUG,"readlen:%d",rtn_len);break;}}//lprintf(MSG_DEBUG,"%s",info->buffer);total_len-=rtn_len;lprintf(MSG_DEBUG,"readlen:%d",rtn_len);if(-1==save_data(info,info->buffer,rtn_len)){return-1;}info->recv_data_len+=rtn_len;}if(total_len!=0){lprintf(MSG_ERROR,"weneedtoread%dbytes,butread%dbytesnow",len,len-total_len);return-1;}}/*接收服務(wù)器發(fā)回的chunked數(shù)據(jù)*/intrecv_chunked_response(http_t*info){longpart_len;//有chunked,contentlength就沒有了do{//獲取這一個(gè)部分的長度fgets(info->buffer,RECV_BUF,info->in);part_len=strtol(info->buffer,NULL,16);lprintf(MSG_DEBUG,"partlen:%ld",part_len);if(-1==read_data(info,part_len))return-1;//讀走后面的兩個(gè)字符if(2!=fread(info->buffer,sizeof(char),2,info->in)){lprintf(MSG_ERROR,"fread\r\nerror:%m");return-1;}}while(part_len);return0;}/*計(jì)算平均下載速度,單位byte/s*/floatcalc_download_speed(http_t*info){intdiff_time=0;floatspeed=0.0;diff_time=info->end_recv_time-info->start_recv_time;/*最小間隔1s,避免計(jì)算浮點(diǎn)數(shù)結(jié)果為inf*/if(0==diff_time)diff_time=1;speed=(float)info->recv_data_len/diff_time;returnspeed;}/*接收服務(wù)器的響應(yīng)數(shù)據(jù)*/intrecv_response(http_t*info){intlen=0,total_len=info->len;if(info->chunked_flag)returnrecv_chunked_response(info);if(-1==read_data(info,total_len))return-1;return0;}/*清理操作*/voidclean_up(http_t*info){if(info->in)fclose(info->in);if(-1!=info->sock)close(info->sock);if(info->save_file)fclose(info->save_file);if(info)free(info);}/*下載主函數(shù)*/inthttp_download(char*url,char*save_path){http_t*info=NULL;chartmp[URI_MAX_LEN]={0};if(!url||!save_path)return-1;//初始化結(jié)構(gòu)體info=malloc(sizeof(http_t));if(!info){lprintf(MSG_ERROR,"mallocfailed");return-1;}memset(info,0x0,sizeof(http_t));info->sock=-1;info->save_path=save_path;//解析urlif(-1==parser_URL(url,info))gotofailed;//連接到serverif(-1==connect_server(info))gotofailed;//發(fā)送http請(qǐng)求報(bào)文if(-1==send_request(info))gotofailed;//接收響應(yīng)的頭信息info->in=fdopen(info->sock,"r");if(!info->in){lprintf(MSG_ERROR,"fdopenerror");gotofailed;}//解析頭部if(-1==parse_http_header(info))gotofailed;switch(info->status_code){caseHTTP_OK://接收數(shù)據(jù)lprintf(MSG_DEBUG,"recvdatanow");info->start_recv_time=time(0);if(-1==recv_response(info))gotofailed;

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論