写基于socket下载 使用epoll多任务

zwy1114860564 2016-06-24 06:56:17
我在写基于socket下载的程序, 使用epoll实现多个任务非阻塞同时下载。测试了一些文件都还好,不过在测试
引用
./multi_download http://sw.bos.baidu.com/sw-search-sp/software/7811f6cde4b/QQ_8.3.18038.0_setup.exe
的时候卡在了100%(死循环), 我查看了一下文件,这个QQ文件不完整。不知道怎么调试,还望哪位大神指明一些方向和步骤。最好成功下载文件,然后写写步骤,“受人以渔”嘛。
#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;
}

问题主要是在download_file这个函数里面。
...全文
115 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧