这次给大家分享一个用C语言实现http的下载器。比如在做OTA升级功能的时候,我们能直接得到的往往只是升级包的链接,需要自己下载。这时候就需要使用http下载器了。
这里分享一个:
功能:
1.支持分块下载。
2.重定向的时候可以下载重定向的页面。
3.要实现的接口在in TTP _ download(char * URL,char * se _ path)。
思路:
1.解析输入的URL,分离出主机、端口号、文件路径等信息。
2.解析主机的DNS
3.填充http请求的报头,并将合同发送给服务器。
4.解析接收到的http头并提取字段信息,如状态代码、内容长度、传输编码等。
(1)如果是普通报头,则进入下一个正常的分组接收过程。
(2)如果状态码是302,则从头中提取重定向地址,并以新地址重新开始下载操作。
(3)如果传输模式为chunked,则分段读取数据并拼接。
(4)如果是404或其他状态代码,则打印错误消息。
缺陷:太多错误处理,让代码看起来不太舒服其他:
1.如何移植到没有文件系统的系统?
在sa_data界面修改保存即可。
2.如何提高下载速度?
增大读写buffer缓冲区改为多线程,使用Range字段分段读取,最后再拼在一起代码:
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *版权所有(C),2016,Leon,保留所有权利。文件名:download . ccoding:utf-8描述:实现简单的http下载功能作者:leonversion: 1.0日期:2016-12-2 10: 49: 32功能:历史:;buffer,RECV_BUF,info-& gt;在);p = strchr(info-& gt;缓冲区,& # 39;');//只需检查http头的第一行是否合法,如果(!p ||!strcasestr(info-& gt;缓冲区,& # 34;HTTP & # 34)){ lprintf(MSG_ERROR,& # 34;错误的http头\ n & # 34);return-1;}信息-& gt;status _ code = atoi(p+1);lprintf(MSG_DEBUG,& # 34;http状态代码:% d \ n & # 34,信息-& gt;状态_代码);//读取并解析http头while(fgets(info->;buffer,RECV_BUF,info-& gt;In)) {//判断头部是否读完if(!strcmp(info-& gt;缓冲区,& # 34;\ r \ n & # 34)){返回0;/*头解析正常*/} lprintf(MSG_DEBUG,& # 34;% s & # 34,信息-& gt;缓冲);//解析长度content-length:554 if(p = strnccasestr(info–>;缓冲区,& # 34;内容长度& # 34;)){ p = strchr(p,& # 39;:');p+= 2;//跳过冒号和后面的空 case信息-& gt;len = atoi(p);lprintf(MSG_INFO,& # 34;内容长度:% d \ n & # 34,信息-& gt;len);} else if(p = strncassestr(info-& gt;缓冲区,& # 34;传输编码& # 34;)){ if(strncassestr(info-& gt;缓冲区,& # 34;chunked & # 34)){ info-& gt;chunked _ flag = 1;} else {/*不支持其他编码传输方式*/ lprintf(MSG_ERROR,& # 34;不支持% s & # 34,信息-& gt;缓冲);return-1;} lprintf(MSG_INFO,& # 34;% s & # 34,信息-& gt;缓冲);} else if(p = strncassestr(info-& gt;缓冲区,& # 34;位置& # 34;)){ p = strchr(p,& # 39;:');p+= 2;//跳过冒号和后面的空case strncpy(info-& gt;位置,p,URI _ MAX _ LEN-1);lprintf(MSG_INFO,& # 34;位置:% s \ n & # 34,信息-& gt;位置);} } lprintf(MSG_ERROR,& # 34;错误的http头\ n & # 34);return-1;/*头解析错误*/}/*保存服务器响应的内容*/int se _ data (http _ t * info,constchar * buf,int len){ int total _ len = len;int write _ len = 0;//如果文件未打开,则打开if(!信息-& gt;保存文件){ info-& gt;se _ file = fopen(info-& gt;保存路径,& # 34;w & # 34);如果(!信息-& gt;se_file) { lprintf(MSG_ERROR,& # 34;fopen %s错误:% m \ n & # 34,信息-& gt;se _ path);return-1;} } while(total _ len){ write _ len = fwrite(buf,sizeof(char),len,info-& gt;se _ file);if(write _ len & lt;len && errno!= EINTR) { lprintf(MSG_ERROR,& # 34;fwrite错误:% m \ n & # 34);return-1;} total _ len-= write _ len;}}/*读取数据*/int read _ data (http _ t * info,int len){ int total _ len = len;int read _ len = 0;int RTN _ len = 0;while(total _ len){ read _ len = MIN(total _ len,RECV _ BUF);// lprintf(MSG_DEBUG,& # 34;需要读取len:% d \ n & # 34;,read _ len);RTN _ len = fread(info-& gt;buffer,sizeof(char),read_len,info-& gt;在);if(RTN _ len & lt;read _ len){ if(ferror(info-& gt;In)) {if(errno == EINTR) /*信号中断读取操作*/{;/*继续而不进行处理*/} else if(errno = = eagain | | errno = = ewouldblock)/* time out */{ lprintf(MSG _ ERROR,& # 34;套接字接收超时:% dms \ n & # 34,RCV _ SND _超时);total _ len-= RTN _ len;lprintf(MSG_DEBUG,& # 34;读取长度:% d \ n & # 34,RTN _ len);打破;} else /*其他错误*/ {lprintf(MSG_ERROR,& # 34;fread错误:% m \ n & # 34);打破;}} else /*读到文件末尾*/ {lprintf(MSG_ERROR,& # 34;套接字被对等方关闭\ n & # 34);total _ len-= RTN _ len;lprintf(MSG_DEBUG,& # 34;读取长度:% d \ n & # 34,RTN _ len);打破;} } // lprintf(MSG_DEBUG,& # 34;% s \ n & # 34,信息-& gt;缓冲);total _ len-= RTN _ len;lprintf(MSG_DEBUG,& # 34;读取长度:% d \ n & # 34,RTN _ len);if(-1 == se_data(info,info-& gt;buffer,RTN _ len)){ return-1;}信息-& gt;recv _ data _ len+= RTN _ len;} if(total_len!= 0) { lprintf(MSG_ERROR,& # 34;我们需要读取%d字节,但现在读取%d字节\ n & # 34,len,len-total _ len);return-1;}}/*接收服务器发回的分块数据*/intre cv _ chunked _ response(http _ t * info){ long part _ len;//带有chunked,内容长度没有do{ //获取该部分的长度fgets(info->;buffer,RECV_BUF,info-& gt;在);part _ len = strtol(info-& gt;缓冲区,空,16);lprintf(MSG_DEBUG,& # 34;部分长度:% ld \ n & # 34,part _ len);if(-1 == read_data(info,part_len))返回-1;//读取最后两个字符if(2!= fread(info-& gt;buffer,sizeof(char),2,info-& gt;in)) { lprintf(MSG_ERROR,& # 34;fread \r\n错误:% m \ n & # 34);return-1;} }而(part _ len);返回0;}/*以byte/s */float calc _ download _ speed(http _ t * info)计算平均下载速度{ int diff _ time = 0;浮动速度= 0.0;diff _ time = info-& gt;end _ recv _ time-info-& gt;开始接收时间;/*最小间隔为1s,以免计算浮点数的结果为INF */if(0 = = diff _ time)diff _ time = 1;速度=(浮点)信息-& gt;接收数据长度/差异时间;返回速度;}/*接收服务器的响应数据*/intre cv _ response(http _ t * info){ int len = 0,total _ len = info-> len;if(info-& gt;chunked_flag)返回recv _ chunked _ response(info);if(-1 == read_data(info,total_len))返回-1;返回0;}/*清理操作*/void clean _ up(http _ t * info){ if(info–>;in)fclose(info-& gt;在);if(-1!= info-& gt;袜子)关闭(信息-& gt;袜子);if(info-& gt;se _ file)fclose(info-& gt;se _ file);if(info)free(info);}/*下载主函数*/int http _ download (char * URL,char * se _ path){ http _ t * info = null;char tmp[URI最大长度]= { 0 };如果(!网址||!se_path)返回-1;//初始化结构info = malloc(sizeof(http _ t));如果(!info) { lprintf(MSG_ERROR,& # 34;malloc失败\ n & # 34);return-1;} memset(info,0x0,sizeof(http _ t));信息-& gt;sock =-1;信息-& gt;se_path =保存路径;//解析URL if (-1 = = parser _ URL (URL,info)) goto失败;//连接到服务器if(-1 = = connect _ server(info))goto失败;//如果(-1 = = send _ request (info)) goto失败,则发送http请求消息;//接收响应的头信息info->;in = FD open(info-& gt;袜子,& # 34;r & # 34);如果(!信息-& gt;in) { lprintf(MSG_ERROR,& # 34;fdopen错误\ n & # 34);goto失败;}//解析头if(-1 = = Parse _ http _ header(info))goto失败;开关(信息->;status _ code){ case HTTP _ OK://Receive data lprintf(MSG _ DEBUG,& # 34;立即接收数据\ n & # 34);信息-& gt;start _ recv _ time = time(0);if(-1 = = recv _ response(info))goto失败;信息-& gt;end _ recv _ time = time(0);lprintf(MSG_INFO,& # 34;接收%d字节\ n & # 34,信息-& gt;recv _ data _ len);lprintf(MSG_INFO,& # 34;平均下载速度:% . 2 fkb/s \ n & # 34;,calc _ download _ speed(info)/1000);打破;Case HTTP_REDIRECT: // Restart这个函数lprintf(MSG_INFO,& # 34;重定向:% s \ n & # 34,信息-& gt;位置);strncpy(tmp,info-& gt;地点,URI _马克斯_伦-1);清理(信息);返回http_download(tmp,se _ path);case HTTP _ NOT _ FOUND://Exit lprintf(MSG _ ERROR,& # 34;找不到页面\ n & # 34);goto失败;打破;默认值:lprintf(MSG_INFO,& # 34;不支持的http代码% d \ n & # 34,信息-& gt;状态_代码);goto失败;} clean up _ info;返回0;失败:clean _ up(info);return-1;* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * http://www . http watch . com/http gallery/chunked/chunked image . aspx & # 34;Test.aspx(2)重定向测试。/a . out & # 34;192 . 168 . 10 . 1/main . html & # 34;Test.txt(3)错误输入测试。/a . out & # 34;32131233"Test.txt(4)根目录输入测试。/a . out & # 34;www.baidu.com/" test . txt(5)端口号访问测试。/a . out & # 34;192 . 168 . 0 . 200:8000/FS _ ac6v 1 . 0 br _ v 15 . 03 . 4 . 12 _ multi _ td01 . bin & # 34;test . txt * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */int main(int argc,char * argv[]){ if(argc & lt;3)return-1;http_download(argv[1],argv[2]);返回0;}版权声明:本文为CSDN博主普朗克常数原创文章,遵循CC 4.0 BY-SA版权协议。转载请附上原出处链接和本声明。原文链接:https://blog.csdn.net/u013401853/article/details/53434167’s嵌入式物联网真的有很多东西要学。不要学错路线和内容,会导致薪资失败!
免费分享一个数据包,差不多150 g以上,学习内容,面子体验,项目都比较新,比较全!估计一条鱼上买至少要几十块。
点击这里找0元,小助手:点击文中蓝色字体获得一个
。
文章链接:https://mp.weixin.qq.com/s/FifcFaBeQ-qhqIfJ9TgnPg
转载自:嵌入式大杂烩
文章来源:C语言实现的http下载器(附代码)
版权声明:本文来源于网络,知识传播免费。版权归原作者所有。请联系我删除作品版权。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。