一、介绍
最近做ota升级需要用到http下载,所以写了一下http下载器
实现流程
1、解析url网址的域名和文件名
2、获取ip地址
3、构建http请求头发送到服务器
4、解析回复
5、下载文件

环境ubuntu linux
c语言
开源链接

main.c

#include <stdio.h>
#include "http_download.h"int main(int argc, char const *argv[])
{if (argc == 1){printf("Input a valid URL \n");exit(0);}else{http_download_file(argv[1]);}return 0;
}

http_download.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include "tcp.h"typedef struct {int status_code;//HTTP/1.1 '200' OKchar content_type[128];//Content-Type: application/gziplong content_length;//Content-Length: 11683079char file_name[256];
}resp_header_def;resp_header_def resp;//头信息#if 0
下载分为以下几个过程1、解析出下载地址中的域名和文件名
2、通过域名获取服务器的IP地址
3、与目标服务器建立连接
4、构建http请求头并将其发送到服务器
5、等待服务器响应然后接收响应头
6、解析响应头, 判断返回码, 分离开响应头, 并且响应的正文内容以字节形式写入文件, 正文内容与头部用两个\n\r分开#endif#define print_LOG(format, ...)     {\printf(format, ##__VA_ARGS__);}//解析网址
//输入url,输出域名、端口、文件名
static int Parsing_urls(char *url, char *domain, int *port, char *filename)
{int i,j,start;char *patterns[] = {"http://", "https://", NULL};*port = 80;//解析域名,就是http://或者https://到/的内容for(i = 0; patterns[i]; i++){if(strncmp(url, patterns[i], strlen(patterns[i])) == 0){start = strlen(patterns[i]);}}//复制域名j = 0;for ( i = start; url[i] != '/' && url[i] != '\0'; i++,j++){domain[j] = url[i];}domain[i] = '\0';//解析端口,冒号后面就是端口char pos = strstr(domain, ":");if(pos){sscanf(pos, ":%d", port);}//如果有端口,需要删掉for ( i = 0; i < (int)strlen(domain); i++){if(domain[i] == ':'){domain[i] = '\0';break;}}//解析下载文件名,/后面就是文件名j = 0;for ( i = start; url[i] != '\0'; i++){if(url[i] == '/'){j = i + 1;memcpy(filename, &url[j], strlen(&url[j]));}}i = strlen(&url[j]);filename[i] = '\0';return 0;
}//通过域名获取ip
static int Domain_to_ip(char *domain, char *ip)
{int i;struct hostent *host = gethostbyname(domain);if(host == 0){*ip = NULL;return -1;}//找到不为空的地址for ( i = 0; host->h_addr_list[i]; i++){strcpy(ip, inet_ntoa( * (struct in_addr*) host->h_addr_list[i]));break;}return 0;
}//构建请求头信息
static int Set_request_headers(char *header, char *url, char *domain)
{int ret = 0;sprintf(header, \"GET %s HTTP/1.1\r\n"\"Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n"\"User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537(KHTML, like Gecko) Chrome/47.0.2526Safari/537.36\r\n"\"Host:%s\r\n"\"Connection:close\r\n"\"\r\n"\,url, domain);ret = strlen(header);return ret;
}
static char *get_response()
{int len, lencnt  = 0,mem_size = 4096;char *rcvbuff = (char *)malloc( mem_size*sizeof(char) );char *response = (char *)malloc( mem_size*sizeof(char) );while((len = tcp_client_rcv(rcvbuff, 1)) != 0){//动态调整缓存大小if((lencnt + len) > mem_size)//判断缓存是否超限{//重新分配内存mem_size *= 2;char *tmp = (char *)realloc(response, mem_size*sizeof(char));if(tmp == NULL){printf("realloc fail\n");exit(-1);}response = tmp;}rcvbuff[len] = '\0';strcat(response, rcvbuff);//找到响应头的头部信息, 两个"\n\r"为分割点int flag = 0;int i = strlen(response) - 1;for (; response[i] == '\n' || response[i] == '\r';i--, flag++);{if (flag == 4)//最多找4次,没找到break;}lencnt += len;}free(rcvbuff);return response;
}//获取回复头的信息
static int get_resp_header(const char *response, resp_header_def *resp)
{//查找HTTP/char *pos = strstr(response, "HTTP/");if (pos)sscanf(pos, "%*s %d", &resp->status_code);//返回状态码pos = strstr(response, "Content-Type:");//返回内容类型if (pos)sscanf(pos, "%*s %s", resp->content_type);pos = strstr(response, "Content-Length:");//内容的长度(字节)if (pos)sscanf(pos, "%*s %ld", &resp->content_length);return 0;
}//打印进度
int progress_bar(int x)
{int i;char tmp[100] = {0};static int x_old = 0;if(x == x_old){return 0;}x_old = x;i = x/2;if(i > 50)i = 50;memset(tmp, '=', i);printf("\r%d%[%s]", x, tmp);fflush(stdout);//立刻输出return 0;
}static int download_writefile()
{int length = 0;int mem_size = 4096;//mem_size might be enlarge, so reset itint buf_len = mem_size;//read 4k each timeint len = 0;int fd = open(resp.file_name, O_CREAT | O_WRONLY, S_IRWXG | S_IRWXO | S_IRWXU);if (fd < 0){print_LOG("Create file failed\n");exit(0);}//申请4k缓存char *buf = (char *)malloc(mem_size * sizeof(char));//读取文件while ((len = tcp_client_rcv(buf, buf_len)) != 0 && length < resp.content_length){write(fd, buf, len);length += len;progress_bar((length*100/resp.content_length));}if (length == resp.content_length){print_LOG("\nDownload successful ^_^\n\n");}else{print_LOG("Finished length:%d,resp.content_length:%d\n",length, resp.content_length);}close(fd);return 0;
}//http下载文件函数
int http_download_file(char *url)
{//char url[2048] = "127.0.0.1";char domain[64] = {0};char ip_addr[16] = {0};int port = 80;char file_name[256] = {0};char header[2048] = {0};char *response = 0;//解析域名Parsing_urls(url, domain, &port, file_name);//解析ipDomain_to_ip(domain, ip_addr);print_LOG("download:\n url:%s\n domain:%s\n ip:%s\n port:%d\n filename:%s\n",url, domain, ip_addr, port, file_name);//发送下载请求Set_request_headers(header, url, domain);if(tcp_client_init(ip_addr,  port) == -1){print_LOG("connect server fail\n");return -1;}tcp_client_send(header, strlen(header));print_LOG("\nsend request\n");//print_LOG("send request:%s\n", header);//解析回复response = get_response();get_resp_header(response, &resp);print_LOG("response:%s\n", response);free(response);strcpy(resp.file_name, file_name);print_LOG("\nres:\n content_length:%d\n file_name:%s\n Content-Type:%s\n", resp.content_length, resp.file_name, resp.content_type);//下载文件print_LOG("\ndownload %s start ...\n", resp.file_name);download_writefile();return 0;
}

download.h

#ifndef __HTTP_DOWNLOAD_H
#define __HTTP_DOWNLOAD_Hint http_download_file(char *url);#endif

tcp.c

//-------tcp相关头文件------
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h> //close()static int socket_fd = 0;//tcp_client_init()
//1、创建socket
//2、配置为客户端
//3、配置要连接的服务器ip和端口以及协议类型
//4、连接服务器
//5、收发数据
//6、关闭连接
int tcp_client_init(char *ip, int port)
{int ret;//1 2socket_fd = socket(AF_INET, SOCK_STREAM, 0);if(socket_fd == -1){printf("create socket fail\n");return -1;}//3 struct sockaddr_in servaddr;servaddr.sin_family = AF_INET;//IPv4协议    servaddr.sin_port = htons(port);//服务器端口号   servaddr.sin_addr.s_addr = inet_addr(ip);//设置服务器//4ret = connect(socket_fd, &servaddr, sizeof(servaddr));if(ret == -1){printf("connect %s fail\n", ip);return -1;}
}//tcp_client_send()
int tcp_client_send(char *buff, int len)
{int ret = 0;ret = write(socket_fd, buff, len);return ret;
}//tcp_client_rcv()
int tcp_client_rcv(char *buff, int len)
{int ret;ret = read(socket_fd, buff, len);return ret;
}//tcp_client_close()
int tcp_client_close()
{close(socket_fd);
}#define CLENT_NUM 2
struct sockaddr_in sSever_c_sd[CLENT_NUM];
static int socket_s_fd = 0;
static int socket_c_fd[CLENT_NUM] = {0};
//tcp_server_init()
#if 0
1、创建socket
2、设置本地ip和端口以及协议类型
3、绑定
4、监听
5、等待客户端连接
6、收发数据
7、关闭连接
#endifint tcp_server_init(int port)
{int ret;//1 socket_s_fd = socket(AF_INET, SOCK_STREAM, 0);if(socket_s_fd == -1){printf("create socket fail\n");return -1;}else{printf("create socket ok\n");}//2 struct sockaddr_in local_addr,c_addr;local_addr.sin_family = AF_INET;//IPv4协议 local_addr.sin_port = htons(port);//服务器端口号 local_addr.sin_addr.s_addr = INADDR_ANY;//设置服务器ip//3ret = bind(socket_s_fd, &local_addr, sizeof(local_addr));if(ret == -1){printf("bind fail\n");close(socket_s_fd);return -1;}else{printf("bind ok\n");}//4ret = listen(socket_s_fd, 3);if(ret == -1){printf("listen fail\n");close(socket_s_fd);return -1;}else{printf("listen ok\n");}//5socklen_t addrlen = 0;while(1){printf("wait client conect\n");socket_c_fd[0] = accept(socket_s_fd, &c_addr, &addrlen);if(addrlen != 0)break;sleep(1);}printf("client conect\n");return 0;
}//tcp_server_send()
int tcp_server_send(char c, char *buff, int len)
{write(socket_c_fd[c], buff, len);
}//tcp_server_rcv()
int tcp_server_rcv(char c, char *buff, int len)
{int ret;ret = read(socket_c_fd[c], buff, len);return ret;
}//tcp_server_close()
int tcp_server_close()
{close(socket_s_fd);
}

tcp.h

#ifndef __TCP_H
#define __TCP_Hint tcp_client_init(char *ip, int port);
int tcp_client_send(unsigned char *buff, int len);
int tcp_client_rcv(unsigned char *buff, int len);
int tcp_client_close();
int tcp_server_init( int port);
int tcp_server_send(char c, unsigned char *buff, int len);
int tcp_server_rcv(char c, unsigned char *buff, int len);
int tcp_server_close();#endif

编译脚本

rm -rf main sscom32.zip
gcc main.c http_download.c tcp.c -o main -w
./main http://xzd.197946.com/sscom32.zip

测试效果
http://xzd.197946.com/sscom32.zip


c语言实现http下载器相关推荐

  1. 【易语言】HTTP下载器 成品+源码

    [易语言]HTTP下载器 成品+源码 易语言简单制作,直接使用即可 直接上成品: 可以直接拿到程序里面使用,可以配合WInform等一起使用 部分参考源码: 成本&源码下载链接:点击下载

  2. 基于C语言实现http下载器

    C语言实现http的下载器. 例:做OTA升级功能时,我们能直接拿到的往往只是升级包的链接,需要我们自己去下载,这时候就需要用到http下载器. 这里分享一个: 功能 1.支持chunked方式传输的 ...

  3. php语言编写一个下载器,PHP实现音乐搜索下载器

    PHP实现音乐搜索下载器 发布时间:2020-07-01 09:57:33 来源:亿速云 阅读:56 作者:Leah 本篇文章为大家展示了PHP实现音乐搜索下载器,代码简明扼要并且容易理解,绝对能使你 ...

  4. 动漫的python语言代码_由Python编写的全异步实现的动漫之家(dmzj)漫画批量下载器(爬虫)...

    DCDownloader 专注于漫画网站.图站等类似形式的内容站点的批量下载器框架. 说明 这个项目最开始是作者编写的一个仅支持某个漫画网站的批量下载器,后来有人提建议说有增加网站的需求,作者便重新梳 ...

  5. Go语言写的解析器(支持json,linq,sql,net,http等)

    Monkey程序语言 Monkey v2.0版本已发布. monkey v2.0 增加了如下内容: 新增 short arrow(->)支持(类似C#的lambda表达式) 增加 列表推导和哈希 ...

  6. atmel c keil 包_Keil C软件与AVR Atmega系列下载器使用

    Keil 作为电子工程师首选,因为他目前通吃C51和STM32的开发平台. 51单片机是8位单片机(AT89C51与STC89C51与AT89S51),AVR Atmega也是属于8位(Atmega3 ...

  7. 解读Windows更新下载器文件列表(推荐)

    Windows更新下载器通过列表文件来寻找补丁地址,其实质是通过xml文件来实现的.其中 主要包括对危险等级程度的描述,下载地址,对该补丁程序的描述.其实很简单.我将其列表文件贴在后面,大家一看就知道 ...

  8. h5禁用浏览器下载视频_【必备】 一键视频下载器插件,非常好用的浏览器插件!...

    ① 资源发布时,版本是最新的.时过境迁,无法保证能否正常使用,请善用公众号搜索功能,请及时下载最新版或留言.② 如果觉得资源对您有用,请收藏.如果觉得资源对他人有用,请转发.③ 如发现"无法 ...

  9. ImageBox网页图片批量下载器

    简介: ImageBox网页图片批量下载器64Bit能够在任意网页上按右键即可批量下载网页上的图片,并且自动生成高清图片相册,还提供了即时下载即时欣赏模式,即边看网页图片,下载专家自动将图片下载下来, ...

  10. 电脑下载软件用什么软件好?安卓手机下载软件用哪个软件好?IDM下载器说:在做的都是弟弟

              大年初五,迎财神,先祝大家新的一年财源滚滚,接下来为大家分享超级经典的IDM下载器,电脑端毫无争议的下载工具,安卓平台idm也是力压群雄,下面就为大家详细分享下: 1:1DM+下载 ...

最新文章

  1. jmeter测试java接口测试_简单易学的测试攻略:JMeter测试Java请求示例
  2. php 对数据转换成tree,PHP 把返回的數據集轉換成Tree樹
  3. linux下imwbr1进程,Linux服务器中木马wnTKYg
  4. [SOJ]连通性问题
  5. 用Python操作Redis
  6. C++ STL学习笔记 : 1. template 模板函数
  7. asterisk版本选择
  8. 查询数据库前十条数据_入门数据分析的一些建议
  9. Java 生态圈知识汇总
  10. 163邮箱怎么申请?手机号怎么申请注册邮箱?
  11. word打印设置相关
  12. 年龄估计:Ordinal Regression
  13. jieba中文分词组件的词性类型
  14. linux下几款可用网盘对比
  15. mysql默认的锁_mysql默认是悲观锁还是乐观锁
  16. 深入浅出的CSS项目开发总结
  17. 英语学习中总结的阅读、段落匹配、选词填空技巧
  18. 【linux内核-源码编译之centos7】
  19. laya 学习抛出事件与接收事件
  20. 基于Skeleton的手势识别:SAM-SLR-v2

热门文章

  1. I2C(smbus、pmbus)和 SPI协议
  2. 【数学】3D数学基础
  3. 微博android源码,新浪微博客户端源码 android
  4. IAR for ARM系列教程(一)_新建软件工程详细过程
  5. 从单张图重建三维人体模型综述(六)
  6. oracle的采购管理模块,ORACLEERP采购管理模块操作手册
  7. 俄罗斯方块是java_俄罗斯方块java
  8. Visio 2019 专业版安装教程
  9. matlab哈明窗带阻,MATLAB数字滤波器程序 Hamming窗带通滤波器
  10. 增程式串联混合动力实际项目模型,本模型基于Cruise软件和Simulink软件共同搭建完成,并实际应用,本资料包包含所有源文件