c語(yǔ)言實(shí)現(xiàn)http下載器的方法
一、介紹
最近做ota升級(jí)需要用到http下載,所以寫(xiě)了一下http下載器
實(shí)現(xiàn)流程
1、解析url網(wǎng)址的域名和文件名
2、獲取ip地址
3、構(gòu)建http請(qǐng)求頭發(fā)送到服務(wù)器
4、解析回復(fù)
5、下載文件
環(huán)境ubuntu linux
c語(yǔ)言
開(kāi)源鏈接
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
下載分為以下幾個(gè)過(guò)程
1、解析出下載地址中的域名和文件名
2、通過(guò)域名獲取服務(wù)器的IP地址
3、與目標(biāo)服務(wù)器建立連接
4、構(gòu)建http請(qǐng)求頭并將其發(fā)送到服務(wù)器
5、等待服務(wù)器響應(yīng)然后接收響應(yīng)頭
6、解析響應(yīng)頭, 判斷返回碼, 分離開(kāi)響應(yīng)頭, 并且響應(yīng)的正文內(nèi)容以字節(jié)形式寫(xiě)入文件, 正文內(nèi)容與頭部用兩個(gè)\n\r分開(kāi)
#endif
#define print_LOG(format, ...) {\
printf(format, ##__VA_ARGS__);}
//解析網(wǎng)址
//輸入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://或者h(yuǎn)ttps://到/的內(nèi)容
for(i = 0; patterns[i]; i++)
{
if(strncmp(url, patterns[i], strlen(patterns[i])) == 0)
{
start = strlen(patterns[i]);
}
}
//復(fù)制域名
j = 0;
for ( i = start; url[i] != '/' && url[i] != '\0'; i++,j++)
{
domain[j] = url[i];
}
domain[i] = '\0';
//解析端口,冒號(hào)后面就是端口
char pos = strstr(domain, ":");
if(pos)
{
sscanf(pos, ":%d", port);
}
//如果有端口,需要?jiǎng)h掉
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;
}
//通過(guò)域名獲取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;
}
//構(gòu)建請(qǐng)求頭信息
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)
{
//動(dòng)態(tài)調(diào)整緩存大小
if((lencnt + len) > mem_size)//判斷緩存是否超限
{
//重新分配內(nèi)存
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);
//找到響應(yīng)頭的頭部信息, 兩個(gè)"\n\r"為分割點(diǎn)
int flag = 0;
int i = strlen(response) - 1;
for (; response[i] == '\n' || response[i] == '\r';
i--, flag++);
{
if (flag == 4)//最多找4次,沒(méi)找到
break;
}
lencnt += len;
}
free(rcvbuff);
return response;
}
//獲取回復(fù)頭的信息
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);//返回狀態(tài)碼
pos = strstr(response, "Content-Type:");//返回內(nèi)容類(lèi)型
if (pos)
sscanf(pos, "%*s %s", resp->content_type);
pos = strstr(response, "Content-Length:");//內(nèi)容的長(zhǎng)度(字節(jié))
if (pos)
sscanf(pos, "%*s %ld", &resp->content_length);
return 0;
}
//打印進(jìn)度
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);
}
//申請(qǐng)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下載文件函數(shù)
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);
//發(fā)送下載請(qǐng)求
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);
//解析回復(fù)
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相關(guān)頭文件------
#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、創(chuàng)建socket
//2、配置為客戶端
//3、配置要連接的服務(wù)器ip和端口以及協(xié)議類(lèi)型
//4、連接服務(wù)器
//5、收發(fā)數(shù)據(jù)
//6、關(guān)閉連接
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協(xié)議
servaddr.sin_port = htons(port);//服務(wù)器端口號(hào)
servaddr.sin_addr.s_addr = inet_addr(ip);//設(shè)置服務(wù)器
//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、創(chuàng)建socket
2、設(shè)置本地ip和端口以及協(xié)議類(lèi)型
3、綁定
4、監(jiān)聽(tīng)
5、等待客戶端連接
6、收發(fā)數(shù)據(jù)
7、關(guān)閉連接
#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協(xié)議
local_addr.sin_port = htons(port);//服務(wù)器端口號(hào)
local_addr.sin_addr.s_addr = INADDR_ANY;//設(shè)置服務(wù)器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
測(cè)試效果
http://xzd.197946.com/sscom32.zip



到此這篇關(guān)于c語(yǔ)言實(shí)現(xiàn)http下載器的文章就介紹到這了,更多相關(guān)c語(yǔ)言http下載器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言用棧實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換為二進(jìn)制的方法示例
這篇文章主要介紹了C語(yǔ)言用棧實(shí)現(xiàn)十進(jìn)制轉(zhuǎn)換為二進(jìn)制的方法,結(jié)合實(shí)例形式分析了C語(yǔ)言棧的定義及進(jìn)制轉(zhuǎn)換使用技巧,需要的朋友可以參考下2017-06-06
C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
這篇文章主要介紹了C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié),包括正弦和雙曲線正弦以及反正弦的函數(shù),需要的朋友可以參考下2015-08-08
c++ 數(shù)字類(lèi)型和字符串類(lèi)型互轉(zhuǎn)詳解
今天小編就為大家分享一篇講解c++ 數(shù)字類(lèi)型和字符串類(lèi)型互轉(zhuǎn)的文章,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-09-09
C++獲取文件哈希值(hash)和獲取torrent(bt種子)磁力鏈接哈希值
這二個(gè)代碼一個(gè)是獲取文件哈希值的,另外一個(gè)是獲取torrent文件磁力鏈接的哈希值2013-11-11
構(gòu)造函數(shù)不能聲明為虛函數(shù)的原因及分析
構(gòu)造函數(shù)不需要是虛函數(shù),也不允許是虛函數(shù),因?yàn)閯?chuàng)建一個(gè)對(duì)象時(shí)我們總是要明確指定對(duì)象的類(lèi)型,盡管我們可能通過(guò)實(shí)驗(yàn)室的基類(lèi)的指針或引用去訪問(wèn)它但析構(gòu)卻不一定,我們往往通過(guò)基類(lèi)的指針來(lái)銷(xiāo)毀對(duì)象2013-10-10
深入理解C語(yǔ)言 static、extern與指針函數(shù)
這篇文章主要介紹了C語(yǔ)言 static、extern與指針函數(shù),有需要的朋友可以參考一下2013-12-12

