大家好,我是冲哥。
本次给大家分享一个C语言实现http的下载器。比如做OTA升级功能时,我们能直接拿到的往往只是升级包的链接,需要我们自己去下载,这时候就需要用到http下载器。
这里分享一个:
功能:
1、支持chunked方式传输的下载
2、被重定向时能下载重定向页面
3、要实现的接口为int http_download(char *url, char *save_path)
思路:
1、解析输入的URL,分离出主机,端口号,文件路径的信息
2、解析主机的DNS
3、填充http请求的头部,给服务器发包
4、解析收到的http头,提取状态码,Content-length, Transfer-Encoding等字段信息
(1)如果是普通的头则进行接下来的正常收包流程
(2)如果状态码为302,则从头里提取出重定向地址,用新的地址重新开始下载动作
(3)如果传送方式是chunked的,则进行分段读取数据并拼接
(4)如果是404或其他状态码则打印错误信息
缺陷:
- 太多错误处理,让代码看起来不太舒服
其他:
1、如何移植到没有文件系统的系统中?
修改sava_data接口里面的保存就好了
2、如何提高下载速度?
- 增大读写buffer缓冲区
- 改为多线程,使用Range字段分段读取,最后再拼在一起
代码:
代码语言:javascript复制/************************************************************
Copyright (C), 2016, Leon, All Rights Reserved.
FileName: download.c
coding: UTF-8
Description: 实现简单的http下载功能
Author: Leon
Version: 1.0
Date: 2016-12-2 10:49:32
Function:
History:
<author> <time> <version> <description>
Leon
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>
#define HOST_NAME_LEN 256
#define URI_MAX_LEN 2048
#define RECV_BUF 8192
#define RCV_SND_TIMEOUT (10*1000) //收发数据超时时间(ms)
typedef struct {
int sock; //与服务器通信的socket
FILE *in; //sock描述符转为文件指针,方便读写
char host_name[HOST_NAME_LEN]; //主机名
int port; //主机端口号
char uri[URI_MAX_LEN]; //资源路径
char buffer[RECV_BUF]; //读写缓冲
int status_code; //http状态码
int chunked_flag; //chunked传输的标志位
int len; //Content-length里的长度
char location[URI_MAX_LEN]; //重定向地址
char *save_path; //保存内容的路径指针
FILE *save_file; //保存内容的文件指针
int recv_data_len; //收到数据的总长度
time_t start_recv_time; //开始接受数据的时间
time_t end_recv_time; //结束接受数据的时间
} http_t;
/* 打印宏 */
#define MSG_DEBUG 0x01
#define MSG_INFO 0x02
#define MSG_ERROR 0x04
static int print_level = /*MSG_DEBUG |*/ MSG_INFO | MSG_ERROR;
#define lprintf(level, format, argv...) do{
if(level & print_level)
printf("[%s][%s(%d)]:"format, #level, __FUNCTION__, __LINE__, ##argv);
}while(0)
#define MIN(x, y) ((x) > (y) ? (y) : (x))
#define HTTP_OK 200
#define HTTP_REDIRECT 302
#define HTTP_NOT_FOUND 404
/* 不区分大小写的strstr */
char *strncasestr(char *str, char *sub)
{
if(!str || !sub)
return NULL;
int len = strlen(sub);
if (len == 0)
{
return NULL;
}
while (*str)
{
if (strncasecmp(str, sub, len) == 0)
{
return str;
}
str;
}
return NULL;
}
/* 解析URL, 成功返回0,失败返回-1 */
/* http://127.0.0.1:8080/testfile */
int parser_URL(char *url, http_t *info)
{
char *tmp = url, *start = NULL, *end = NULL;
int len = 0;
/* 跳过http:// */
if(strncasestr(tmp, "http://"))
{
tmp = strlen("http://");
}
start = tmp;
if(!(tmp = strchr(start, '/')))
{
lprintf(MSG_ERROR, "url invaildn");
return -1;
}
end = tmp;
/*解析端口号和主机*/
info->port = 80; //先附默认值80
len = MIN(end - start, HOST_NAME_LEN - 1);
strncpy(info->host_name, start, len);
info->host_name[len] = '