69,373
社区成员
发帖
与我相关
我的任务
分享
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/epoll.h>
#define MAXLINE 4096 /* max line length */
#define MAX_RECV_SIZE 1440
#define bool int
#define false 0
#define true 1
const int EXIT_FAILURE = 1;
char req_addr[256] = "";
char req_file[1024] = "";
char req_serv[256] = "";
char filename[256] = "";
char buf_send[4*1024] = "";
char buf_recv[10][10*1024];
int fds[10];
int sockfds[10];
int flags[10];
int nbytes[10];
int filesizes[10];
int cursizes[10];
// 映射sockfd到文件, 用于socket关闭时,重新打开一个socket, sockfd < 1024
int sockfds_map_indexs[1024];
static void
err_doit(int errnoflag, const char *fmt, va_list ap)
{
int errno_save;
char buf[MAXLINE];
errno_save = errno; /* value caller might want printed */
vsprintf(buf, fmt, ap);
if (errnoflag)
sprintf(buf+strlen(buf), ": %s", strerror(errno_save));
strcat(buf, "\n");
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr); /* SunOS 4.1.* doesn't grok NULL argument */
return;
}
void
/* $f err_sys $ */
err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, fmt, ap);
va_end(ap);
exit(1);
}
void Close(int fd)
{
if (close(fd) == -1){
err_sys("close error!");
}
}
void
Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
if (setsockopt(fd, level, optname, optval, optlen) < 0)
err_sys("setsockopt error");
}
void Send(int fd, const void *ptr, size_t nbytes, int flags)
{
if (send(fd, ptr, nbytes, flags) != (ssize_t)nbytes)
err_sys("send error");
}
int tcp_connect(const char *host, const char *serv)
{
int sockfd, n;
struct addrinfo hints, *res, *ressave;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0)
err_sys("tcp_connect error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res;
do {
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sockfd < 0)
continue; /* ignore this one */
Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0)
break; /* success */
Close(sockfd); /* ignore this one */
} while ( (res = res->ai_next) != NULL);
if (res == NULL) /* errno set from final connect() */
err_sys("tcp_connect error for %s, %s", host, serv);
freeaddrinfo(ressave);
return(sockfd);
}
/* end tcp_connect */
/*
* We place the wrapper function here, not in wraplib.c, because some
* XTI programs need to include wraplib.c, and it also defines
* a Tcp_connect() function.
*/
int
Tcp_connect(const char *host, const char *serv)
{
return(tcp_connect(host, serv));
}
void Writen(int fd, char *buff, int nbytes){
int done = 0, index = 0;
while(nbytes > 0){
done = write(fd, buff + index, nbytes);
if(done < 0)
err_sys("write error!\n");
index += done;
nbytes -= done;
}
}
int url_parser(char *url){
char *ptr = NULL;
char *pcolon = NULL;
int len = 0;
ptr = strstr(url, "http://");
if(NULL != ptr)
url = ptr + strlen("http://");
ptr = strchr(url, '/');
len = strlen(url);
memset(req_addr, 0x0, sizeof(req_addr));
memset(req_serv, 0x0, sizeof(req_serv));
pcolon = strchr(url, ':');
if(NULL == pcolon || strchr(ptr+1, '/') - pcolon < 0)
{
strncpy(req_addr, url, ptr - url);
sprintf(req_serv, "80");
}
else
{
strncpy(req_addr, url, pcolon - url);
strncpy(req_serv, pcolon + 1, ptr - pcolon - 1);
}
memset(req_file, 0x0, sizeof(req_file));
strcpy(req_file, ptr);
int i = len - 1;
for(; i >= 0; --i){
if('/' == url[i]){
strcpy(filename, url + i + 1);
break;
}
}
return 0;
}
void set_download_http_head(char *req_addr, char *req_file, char *range)
{
char buf[64];
memset(buf_send,0x0, sizeof(buf_send));
sprintf(buf_send, "GET %s",req_file);
//HTTP/1.1\r\n 前面需要一个空格
strcat(buf_send," HTTP/1.1\r\n");
strcat(buf_send, "Host: ");
strcat(buf_send, req_addr);
//strcat(g_buf_send, ":");
//strcat(g_buf_send, PORT);
sprintf(buf, "\r\nRange: bytes=%s",range);
strcat(buf_send,buf);
//strcat(buf_send, "\r\nKeep-Alive: 200");
strcat(buf_send,"\r\nConnection: Keep-Alive\r\n\r\n");
}
//获取Content-Range
int get_value_of_content_range(char *revbuf)
{
char *p1 = NULL, *p2 = NULL;
int HTTP_Body = 0;//内容体长度
p1 = strstr(revbuf,"Content-Range");
if(p1 == NULL)
return -1;
else
{
p2 = strchr(p1, '/');
HTTP_Body = atoi(p2+1);
return HTTP_Body;
}
}
void setnonblocking(int sockfd)
{
int opts;
opts = fcntl(sockfd,F_GETFL);
if(opts<0)
err_sys("fcntl(sock,GETFL)");
opts = opts|O_NONBLOCK;
if(fcntl(sockfd,F_SETFL,opts)<0)
err_sys("error : fcntl(sock,SETFL,opts)");
}
void download_files(int vc, char ** ve)
{
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int nfds, epollfd, cursize;
int nbyte, index, count = 0;
char *p = NULL;
bool flag = true;
char range[6];
epollfd = epoll_create(10);
if (epollfd == -1)
err_sys("epoll_create");
// 因为这里是直接使用main函数的头, 0表示的文件名
int i = 1;
for(; i < vc; ++i)
{
url_parser(ve[i]);
sockfds[i] = Tcp_connect(req_addr, req_serv);
if((fds[i]=open(filename,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR))==-1)
err_sys("Open %s Error:%s\n",filename,strerror(errno));
// 设置非阻塞
setnonblocking(sockfds[i]);
// 设置映射
sockfds_map_indexs[sockfds[i]] = i;
off_t cursize;
cursize = lseek(fds[i], 0, SEEK_END);
memset(range, 0x0, sizeof(range));
sprintf(range,"%d-", cursize);
set_download_http_head(req_addr, req_file, range);
Send(sockfds[i], buf_send, strlen(buf_send), 0);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sockfds[i];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfds[i],
&ev) == -1)
err_sys("epoll_ctl: conn_sock");
flags[i] = false;
memset(buf_recv[i], 0x0, sizeof(buf_recv[i]));
}
for(;;)
{
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1)
err_sys("epoll_pwait");
int i = 0;
for(; i < nfds; ++i)
{
index = sockfds_map_indexs[ events[i].data.fd ];
flag = true;
while(flag)
{
nbyte = recv(events[i].data.fd, buf_recv[index] + nbytes[index], sizeof(buf_recv[index]) - nbytes[index], 0);
if(nbyte < 0)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
close(events[i].data.fd);
url_parser(ve[index]);
sockfds[index] = Tcp_connect(req_addr, req_serv);
memset(range, 0x0, sizeof(range));
sprintf(range,"%d-", cursizes[index]);
set_download_http_head(req_addr, req_file, range);
Send(sockfds[index], buf_send, strlen(buf_send), 0);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = sockfds[index];
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfds[index],
&ev) == -1)
err_sys("epoll_ctl: conn_sock");
flag = false;
}
else if(0 == nbyte)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
close(events[i].data.fd);
close(fds[index]);
printf("one download success\n");
flag = false;
}
else
{
nbytes[index] += nbyte;
if(sizeof(buf_recv[index]) != nbytes[index])
flag = false;
if(false == flags[index])
{
p = strstr(buf_recv[index], "\r\n\r\n");
if(NULL != p)
{
flags[index] = true;
filesizes[index] = get_value_of_content_range(buf_recv[index]);
Writen(fds[index], p+4, nbytes[index] - (p - buf_recv[index]) - 4);
cursizes[index] += nbytes[index] - (p - buf_recv[index]) - 4;
nbytes[index] = 0;
memset(buf_recv[index], 0x0, sizeof(buf_recv[index]));
}
}
else
{
Writen(fds[index], buf_recv[index], nbyte);
cursizes[index] += nbyte;
printf("%s download %.f%%\n", ve[index], cursizes[index] * 100.0 / filesizes[index]);
nbytes[index] = 0;
memset(buf_recv[index], 0x0, sizeof(buf_recv[index]));
if(cursizes[index] == filesizes[index])
break;
}
}
}
if(cursizes[index] == filesizes[index])
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, events[i].data.fd, &ev);
close(events[i].data.fd);
close(fds[index]);
++count;
printf("%s download success\n", ve[index]);
}
}
if(count == vc - 1)
{
printf("%d file download succeed \n", count);
break;
}
}
}
int main(int argc, char ** argv)
{
download_files(argc, argv);
return 0;
}