linux c http下载文件带进度条

/*************************************************************************
  > File Name: http.c
  > Author: 夜晚不懂天的白 
  > Mail: [email protected]
  > Created Time: 2018年06月30日 星期六 19时05分15秒
 ************************************************************************/
#include <sys/ioctl.h>
#include <poll.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <ctype.h>
#include <pthread.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <strings.h>
#include <string.h>
#include <errno.h>

typedef struct HTTP_INFO{
	unsigned long file_size;
	char content_type[256];
	char file_name[256];
	char ip_addr[INET_ADDRSTRLEN];
	char aliases[256];
	int status_code;
}HTTP_INFO;

typedef struct HOST_INFO{
	char host_name[256];
	char file_path[1024];
	unsigned short port;
	char new_name[256];
}HOST_INFO;

HOST_INFO host_info;
HTTP_INFO http_info;

void print_error(const char *msg)
{
	perror(msg);
	exit(1);
}

//获取服务端地址和别名
int get_http_info(const char *host_name,HTTP_INFO *http_info)
{
	//判断是否为IP地址
	int a,b,c,d;
	if (sscanf(host_name,"%d.%d.%d.%d",&a,&b,&c,&d) == 4){
		if (a >= 0x0 && a <= 0xff && b >= 0x0 && b <= 0xff && c >=0 && c <= 0xff && d >=0 && d <= 0xff){
			strcpy(http_info->ip_addr,host_name);
			strcpy(http_info->aliases,"no aliases");
			return 0;
		}
	}
	struct hostent *phost = gethostbyname(host_name);
	if (!phost)
		return -1;
	/*char **ptr = NULL,temp[INET_ADDRSTRLEN];
	for (ptr = phost->h_addr_list;*ptr != NULL;ptr++)
	{
		bzero(temp,sizeof(temp));
		printf("对方服务器地址:%s\n",inet_ntop(AF_INET,*ptr,temp,sizeof(temp)));
	}

	for (ptr = phost->h_aliases;*ptr != NULL;ptr++)
	{
		printf("域名别名:%s\n",*ptr);
	}*/
	inet_ntop(AF_INET,phost->h_addr_list[0],http_info->ip_addr,sizeof(http_info->ip_addr));
	if (*phost->h_aliases != NULL)
		strcpy(http_info->aliases,phost->h_aliases[0]);
	else
		strcpy(http_info->aliases,"no aliases");
	return 0;
}

//发送http报文请求
int send_http_header(int sfd,HOST_INFO host_info)
{
	char http_header[BUFSIZ];
	bzero(http_header,sizeof(http_header));
	sprintf(http_header,\
			"GET %s HTTP/1.1\r\n"\
			"Host: %s\r\n"\
			"Connection: Keep-Alive\r\n"\
			"Content-Type: application/octet-stream\r\n"\
			"\r\n",host_info.file_path,host_info.host_name);
	return write(sfd,http_header,strlen(http_header));
}

//解析http报文头
int parse_http_header(int sfd,HTTP_INFO *http_info)
{
	char buffer[BUFSIZ],temp[BUFSIZ],*ptr;
	bzero(buffer,sizeof(buffer));
	bzero(temp,sizeof(temp));
	int len,n = 0;
	while((len = read(sfd,buffer,1)) != 0){
		temp[n] = *buffer;
		if (*buffer == '\n'){
			ptr = strstr(temp,"HTTP/1.1");
			if (ptr != NULL){
				ptr = strchr(ptr,' ');
				ptr++;
				http_info->status_code = atoi(ptr);
			}
			ptr = strstr(temp,"Content-Length:");
			if (ptr != NULL){
				ptr = strchr(ptr,':');
				ptr++;
				http_info->file_size = strtoul(ptr,NULL,10);
			}
			ptr = strstr(temp,"Content-Type:");
			if (ptr != NULL){
				ptr = strchr(ptr,':');	
				ptr++;
				strcpy(http_info->content_type,ptr);
				http_info->content_type[strlen(ptr) - 1] = '\0';
			}
			//printf("%s",temp);
			if (temp[0] == '\r' && temp[1] == '\n')
				break;
			bzero(temp,sizeof(temp));
			n = -1;
		}
		n++;
	}
	return sfd;
}

int connect_server(const char *ip_addr,unsigned short port)
{
	int sfd = socket(AF_INET,SOCK_STREAM,0);
	if (sfd == -1)
		print_error("socket");

	struct sockaddr_in serv_addr;
	serv_addr.sin_family = AF_INET;

	//默认80端口
	serv_addr.sin_port = htons(port);

	//根据域名获取到ip地址
	int ret = inet_pton(AF_INET,ip_addr,&serv_addr.sin_addr.s_addr);
	if (ret == -1)
		print_error("inet_pton");
	
	int flags = fcntl(sfd,F_GETFL,0);

	flags |= O_NONBLOCK;
	fcntl(sfd,F_SETFL,flags);

	//连接服务器
	ret = connect(sfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
	if (ret != 0 && errno == EINPROGRESS){
		printf("connecting server,please wait for 10 seconds\n");
		struct pollfd pfd;
		socklen_t optlen;
		int optval,timeout = 10 *1000;
		pfd.fd = sfd;
		pfd.events = POLLOUT;
		
		if ((ret = poll(&pfd,1,timeout)) == 1){
			//建立连接成功
			optlen = sizeof(optval);
			ret = getsockopt(sfd,SOL_SOCKET,SO_ERROR,&optval,&optlen);
			errno = optval;
			ret = optval == 0 ? 0 : -1;
			if (!ret)
				printf("connection %s\n",strerror(errno));
		}
		else if (!ret){
			//连接超时
			errno = ETIMEDOUT;
			ret = -1;
		}
		else
			print_error("poll failed");
	}
	flags &= ~O_NONBLOCK;
	fcntl(sfd,F_SETFL,flags);
	return ret == -1 ? -1 : sfd;
}

//打印进度条
void print_progress_bar(const char *file_name,float sum,float file_size)
{
	float percent = (sum / file_size) * 100;
	char *sign = "#";
	if ((int)percent != 0){
		sign = (char *)malloc((int)percent + 1);
		strncpy(sign,"####################################################",(int) percent);
	}
	printf("%s %7.2f%% [%-*.*s] %.2f/%.2f mb\r",file_name,percent,50,(int)percent / 2,sign,sum / 1024.0 / 1024.0,file_size / 1024.0 / 1024.0);
	if ((int)percent != 0)
		free(sign);
	fflush(stdout);
}

unsigned long download(int sfd,HOST_INFO host_info,HTTP_INFO http_info)
{
	umask(0111);
	int len;
	unsigned long sum = 0;
	char buffer[BUFSIZ] = { 0 };
	int fd = open(host_info.new_name,O_CREAT | O_WRONLY | O_TRUNC,0777);
	if (fd == -1)
		print_error("open");
	while((len = read(sfd,buffer,sizeof(buffer))) > 0){
		write(fd,buffer,len);
		sum+=len;
		print_progress_bar(host_info.new_name,(float)sum,(float)http_info.file_size);
		if (http_info.file_size == sum){
			printf("\n");
			break;
		}
	}
	close(fd);
	close(sfd);
	return sum;	
}

//解析url
void parse_http_url(char *url,HOST_INFO *host_info)
{
	char *protocol[] = {"http://","https://",NULL};
	int i,len = 0;

	//删除http/https协议头
	for (i = 0;protocol[i] != NULL;i++)
		if (!strncmp(url,protocol[i],strlen(protocol[i]))){
			len = strlen(protocol[i]);
			break;
		}

	
	char *ptr = strchr(url + len,':');
	if (ptr != NULL){
		//提取域名
		strncpy(host_info->host_name,url + len,strlen(url + len) - strlen(ptr));
		//提取端口
		sscanf(++ptr,"%5hu",&host_info->port);
		//判断端口是否为有效端口
		if (host_info->port > 65535){
			printf("invalid port\n");
			exit(1);
		}
		//提取文件路径
		while(*ptr != '/')
			ptr++;
		strcpy(host_info->file_path,ptr);
		//提取文件名
		ptr = strrchr(host_info->file_path,'/');
		ptr++;
		strcpy(host_info->new_name,ptr);
	}
	else{
		ptr = strchr(url + len,'/');
		//提取域名
		strncpy(host_info->host_name,url + len,strlen(url + len) - strlen(ptr));
		//设置为默认端口
		host_info->port = 80;
		//提取文件路径
		strcpy(host_info->file_path,ptr);
		//提取文件名
		ptr = strrchr(host_info->file_path,'/');
		ptr++;
		strcpy(host_info->new_name,ptr);
	}
}

//获取下载的文件大小
unsigned long get_file_size(const char *file_name)
{
	struct stat buf;
	if (stat(file_name,&buf) == -1)
		return 0;
	else
		return buf.st_size;
}

//get请求
int http_get(char *url,char *new_name)
{
	bzero(&host_info,sizeof(host_info));
	bzero(&http_info,sizeof(http_info));

	//解析url
	parse_http_url(url,&host_info);
	
	if (new_name != NULL)
		strcpy(host_info.new_name,new_name);

	//根据域名获取IP地址
	if (get_http_info(host_info.host_name,&http_info) == -1)
		print_error("gethostbyname");
	
	//连接服务器
	int sfd = connect_server(http_info.ip_addr,host_info.port);
	if (sfd == -1)
		print_error("connect failed");
	printf("waiting for http response\n");
	//发送http报文
	int	ret = send_http_header(sfd,host_info);	
	if (ret == -1)
		print_error("write");

	//解析http报头
	sfd = parse_http_header(sfd,&http_info);
	
	printf("http response:\n\tstatus code: %d\n",http_info.status_code);
	printf("\thost: %s:%hu\n",http_info.ip_addr,host_info.port);
	printf("\taliases: %s\n",http_info.aliases);
	printf("\tcontent-type:%s\n",http_info.content_type);
	if (http_info.file_size > 1024*1024)	
		printf("\tcontent-length: %.2f mb\n",(float)http_info.file_size / 1024.0 / 1024.0);
	else
		printf("\tcontent-length: %lu bytes\n",http_info.file_size);

	if (http_info.status_code != 200)
		printf("warning:not found file %s\n",host_info.new_name);
	
	printf("are you sure to download this file?\n");
	printf("please enter any key to continue\n");
	getchar();
	
	//下载文件
//	printf("%s\n",host_info.new_name);
	unsigned long download_size = download(sfd,host_info,http_info);
	unsigned long file_size = get_file_size(host_info.new_name);
//	printf("dowload_size %d\n",download_size);
//	printf("file_size %lu\n",file_size);
	if (download_size != file_size){
		printf("download %s failure\n",host_info.new_name);
		remove(host_info.new_name);
	}
	return 0;
}

//如果按下了ctrl+c
void sig_handler(int num)
{
	if (num == SIGINT){
		printf("\ndownload %s abort\n",host_info.new_name);
		if (!access(host_info.new_name,F_OK))
			remove(host_info.new_name);
		putc('\n',stdout);
		exit(0);
	}
}


int main(int argc,char *argv[])
{
	if (argc < 2){
		printf("Usage:\n");
		printf("./download url\n");
		printf("./download url \"new file name\"\n");
		return 0;
	}
	//捕捉ctrl+c
	signal(SIGINT,sig_handler);
	http_get(argv[1],argv[2]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_31629063/article/details/80846673