c语言实现http下载器的方法

一、介绍

最近做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' OK
    char content_type[128];//Content-Type: application/gzip
    long content_length;//Content-Length: 11683079
    char 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 it
    int buf_len = mem_size;//read 4k each time
    int 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);

    //解析ip
    Domain_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_H

int 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 2
	socket_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);//设置服务器
	//4
	ret = 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、关闭连接
#endif

int 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
	//3
	ret = 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");
	}
	//4
	ret = listen(socket_s_fd, 3);
	if(ret == -1)
	{
		printf("listen fail\n");
		close(socket_s_fd);
		return -1;
	}
	else
	{
		printf("listen ok\n");
	}
	//5
	socklen_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_H

int 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下载器的文章就介绍到这了,更多相关c语言http下载器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c语言http请求解析表单内容

    cgi.h 复制代码 代码如下: #ifndef CGI_H#define CGI_H #include <stdio.h>#include <string.h>#include <stdlib.h> typedef struct Node{ char *name; char *value; struct Node *next;}Node; typedef struct Index{ Node *head; char *buffer;}Index; Index *get

  • 如何利用C语言实现最简单的HTTP服务器详解

    此段代码的特点 <h1>Hello!</h1> 如何编译运行? 编译: gcc -o hello_server hello_server.c 运行: ./hello_server 请求: curl http://localhost:8888/any 源文件 hello_server.c #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/so

  • c语言实现http下载器的方法

    一.介绍 最近做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) { pri

  • python 并发下载器实现方法示例

    本文实例讲述了python 并发下载器实现方法.分享给大家供大家参考,具体如下: 并发下载器 并发下载原理 from gevent import monkey import gevent import urllib.request # 有耗时操作时需要 monkey.patch_all() def my_downLoad(url): print('GET: %s' % url) resp = urllib.request.urlopen(url) data = resp.read() print

  • 基于C语言实现http下载器

    目录 功能 思路 缺陷 代码 C语言实现http的下载器. 例:做OTA升级功能时,我们能直接拿到的往往只是升级包的链接,需要我们自己去下载,这时候就需要用到http下载器. 这里分享一个: 功能 1.支持chunked方式传输的下载 2.被重定向时能下载重定向页面 3.要实现的接口为int http_download(char *url, char *save_path) 思路 1.解析输入的URL,分离出主机,端口号,文件路径的信息 2.解析主机的DNS 3.填充http请求的头部,给服务器

  • 易语言源码下载器软件制作

    易语言源码一键下载器 1.打开易语言 画控件,一个是菜单版,一个是超级按钮的 2.写代码 3.运行效果 总结:以上就是关于用易语言编写下载器的大概步骤,感谢大家的学习和对我们的支持.

  • 使用 go 实现多线程下载器的方法

    目录 1.多线程下载原理 2.构造一个下载器 2.1 为下载器提供初始化方法 3.实现下载综合调度逻辑 3.1 下载文件分段 3.2 子线程下载函数 4. 保存下载文件函数 5.完整代码 本篇文章我们用Go实现一个简单的多线程下载器. 1.多线程下载原理 通过判断下载文件链接返回头信息中的 Accept-Ranges 字段,如果为 bytes 则表示支持断点续传. 然后在请求头中设置 Range 字段为 bytes=[start]-[end],以请求下载文件的分段部分,然后将所有分段合并为一个完

  • 易语言无法定位链接器解决方法

    易语言开发环境的诞生,影响了众多编程爱好者的关注的追捧.的确,很多编程爱好者在使用易语言的同时产生了很多的困惑,这些困惑很多,比如易语言无法定位链接器. 1.首先,打开易语言,创建一个"Windows窗口程序"空白工程,操作如下: 2.进入窗口界面以后,我们不编写任何的代码,就只有一个空白的窗口.如图: 3.然后,点击工具条中的"运行"按钮或者按下"F5"键运行程序,如图: 4.从运行结果可以看出,程序是没有问题的.那么,我们来静态编译一下,看看

  • 易语言多线程多任务下载器的代码

    多线程多任务下载模块源码 1.DLL命令表 .版本 2 .DLL命令 InternetGetConnectedStateHTTP, , "Wininet.dll", "InternetGetConnectedState", 公开 .参数 lpdwFlags, 整数型, 传址 .参数 dwReserved, 整数型 .DLL命令 InternetOpenAHTTP, 整数型, "Wininet.dll", "InternetOpenA&q

  • python动态视频下载器的实现方法

    这里向大家分享一下python爬虫的一些应用,主要是用爬虫配合简单的GUI界面实现视频,音乐和小说的下载器.今天就先介绍如何实现一个动态视频下载器. 爬取电影天堂视频 首先介绍的是python爬取电影天堂网站的视频(包括电影,电视剧,综艺等),主要是用selenium动态网页技术加上简单的爬虫技术. (1)电影网站首页面地址:https://www.dytt8.net/ (2)用到的技术:selenium模拟浏览器运行. (3)首先要安装配置selenium库和不同浏览器和该库配合的插件.这里安

  • 易语言制作图标提取器方法

    图标提取器网上也很多这样的工具,这里我们就用易语言自己做一个,本人也是一边学边应用,代码不一定好,只给大家参考学习用 1.我们先做好图标提取器的界面,添加两个按钮,两个编辑框,适当更改一下窗口的标题和标签说明 2.接着增加一个图片框,设置为凹入式,背景颜色为白,添加标签说明接着增加一个图片框,设置为凹入式,背景颜色为白,添加标签说明 3.接着再添加两个通用对话框,通用对话模型1类型设置为"保存文件",通用对话框2类型设置为"打开文件" 4.界面做好了,再看代码部分,

  • flvplayer.swf flv视频播放器使用方法

    一.直接在html文件中加载: 复制代码 代码如下: <div id="FlashFile"> <object type="application/x-shockwave-flash" width="470px" height="403px" data="flvplayer.swf?file=movies/company.flv"> <param name="movi

随机推荐