`
fxsjy
  • 浏览: 35586 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

C语言写的多线程下载器

    博客分类:
  • c
阅读更多
1.软件介绍

qdown是一款开源的HTTP多线程下载软件。
特点:多线程,支持服务器重定向,支持断点续传。

平台:Solaris/FreeBSD/Windows(CygWin)

作者:小孙

2.如何使用

usage: qdown URL [thread_amount] [save as]
example: qdown http://www.baidu.com/img/logo.gif 5 /home/sunjoy/log.gif

3.如何编译
On Solaris: cc -lsocket -lnsl qdown.c
On FreeBSD: gcc -pthread qdown.c
或者用sunstudio打开工程文件编译

4.基本原理

4.1 多线程原理
HTTP协议规定在请求报头中加入Range: bytes=%d-%d (%d代表整数)来下载指定范围的块儿,
因此根据文件的总大小,qdown开启多个线程分别下载各个部分,最终完成下载整个文件。


4.2 服务器重定向
很多情况下,当客户端发起GET请求后,服务器可能通过Location: xxxxx来告诉客户端重定向
到新的URL,当qdown遇到这种情况时会去下载新的URL指定的文件。qdown最多允许5次重定向。


4.3 断点续传
由于程序被中断或者网络故障等原因可能导致一个文件没有下载完全。qdown在下载过程中会
维护一个.cfg文件来记录个线程的下载情况,当重新下载时,qdown会根据.cfg文件的记录从
上次断掉的地方开始下载。

5.改进方向
预计在下一版本中加入对FTP URL的支持


/**
** description:qdown is a multithread downloader
** author:Sunjoy
** email:fxsjy @@@ yahoo.com.cn
** from:ICT.CAS.
** date:2007-9-10
**
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MAX_THREAD 100

typedef struct URLInfo
{
    char schema[8];
    char host[256];
    char host_name[256];
    unsigned int port;
    char file[256];
}URLInfo;

typedef struct Connection
{
    int sock;
    URLInfo url_info;
    int avaliable;
}Connection;

typedef struct Resource
{
    char file_url[256];
    int file_size;
    char file_name[256];
}Resource;

typedef struct ThreadArg
{
    Resource* res;
    int start_pos;
    int limit;
    int no;
}ThreadArg;

typedef struct BreakPoint
{
    int downloaded;
    int thread_amount;
    int tasks[MAX_THREAD][2];
    
}BreakPoint;

pthread_mutex_t g_mut;
int g_total=0;
int g_downloaded=0;
BreakPoint g_breakpoint;

URLInfo parse_url(const char *url);
Connection open_url(const char * url);
Resource get_resource(const char *url);
void join_url(const char* old_url,const char* redirect,char * new_url);
void download(const char* url,int thread_amount,const char* file_name);
void* download_part(void* args);
void* monitor(void *args);
void store_breakpoint(char * cfgName);

void store_breakpoint(char * cfgName)
{
    int z;
    FILE* f;
    f=fopen(cfgName,"w");
    fprintf(f,"%d\n",g_breakpoint.downloaded);
    fprintf(f,"%d\n",g_breakpoint.thread_amount);
    for(z=0;z<g_breakpoint.thread_amount;z++){
       fprintf(f,"%d-%d\n",g_breakpoint.tasks[z][0],g_breakpoint.tasks[z][1]);
    }
    fclose(f);
}

void join_url(const char* old_url,const char* redirect,char * new_url)
{
    char stack1[256][256]={0},stack2[256][256]={0};
    int i=0,j=0,p1=0,p2=0;
    char seg[256]={0};
    URLInfo temp_urlinfo;
    
    memset(new_url,0,sizeof(new_url));
    if(strstr(redirect,"://")!=NULL){
        strcpy(new_url,redirect);
    }
    else{
        while(1){
            while(redirect[i]!='/' && redirect[i]!=0){
                seg[j++]=redirect[i++];
            }    
            strcpy(stack1[p1++],seg);
            memset(seg,0,sizeof(seg));
            j=0;
            if(redirect[i]==0)
                break;
            i++;
        }
        for(i=0;i<p1;i++){
            if(!strcmp(stack1[i],"..") && p2>-1)
                p2--;
            else if(strcmp(stack1[i],".")){
                strcpy(stack2[p2++],stack1[i]);
            }
        }
        //printf("##%s\n",stack2[0]);
   
        if(!strcmp(stack2[0],"")){
            temp_urlinfo=parse_url(old_url);
            sprintf(new_url,"%s://%s:%d/",temp_urlinfo.schema,temp_urlinfo.host,temp_urlinfo.port);          
        }
        else{
            i=strlen(old_url)-1;
            while(old_url[i]!='/')
                i--;
            //printf("##%c\n",old_url[i]);
            strncpy(new_url,old_url,i+1);
            new_url[i+1]=0;
        }
        //printf("##%s\n",new_url);
        for(j=0;j<p2-1;j++){
            strcat(new_url,stack2[j]);
            strcat(new_url,"/");
        }
        strcat(new_url,stack2[p2-1]);
    }
}

URLInfo parse_url(const char* url){
    int i=0,j=0;
    char schema[8]={0};
    char host[256]={0};
    char port[8]={0};
    char file[256]={0};
    char IP[32]={0};
    URLInfo url_info;
    struct hostent* hptr;
    
    while(url[i]!=':'){
        schema[j++]=url[i++];
    }

    for(i+=3,j=0;url[i]!=':' && url[i]!='/' && url[i]!=0;){
        host[j++]=url[i++];
    }
    
    if(url[i]==':'){
        for(i+=1,j=0;url[i]!='/';){
            port[j++]=url[i++];
        }
        sscanf(port,"%d",&url_info.port);
    }
    else{
        url_info.port=80;
    }
    
    if(url[i]!=0){
        for(j=0;url[i]!=0;){
            file[j++]=url[i++];
        }
    }
    else{
        file[0]='/';
    }
    
    strcpy(url_info.schema,schema);
    strcpy(url_info.file,file);
    strcpy(url_info.host_name,host);
    hptr=gethostbyname(host);
   
    if(hptr!=NULL){
        strcpy(url_info.host,
            inet_ntop(hptr->h_addrtype,*(hptr->h_addr_list),IP,sizeof(IP))
        );
    }
    //printf("%s\n",url_info.host);
    return url_info;
}
Connection open_url(const char* url){
    Connection conn;
    struct sockaddr_in remote_addr,local_addr;

    conn.avaliable=0;
    conn.url_info=parse_url(url);
    
    local_addr.sin_family=AF_INET;
    local_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    local_addr.sin_port=htons(0);
    remote_addr.sin_family=AF_INET;
    remote_addr.sin_addr.s_addr=inet_addr(conn.url_info.host);
    remote_addr.sin_port=htons(conn.url_info.port);
    
    conn.sock=socket(AF_INET,SOCK_STREAM,0);
    if(bind(conn.sock,
        (struct sockaddr*)&local_addr,
        sizeof(local_addr))<0){
            printf("bind error\n");
    }
    
    
    
    if(conn.sock){
        if(
            connect(conn.sock,(struct sockaddr*)&remote_addr,sizeof(remote_addr))!=-1
        ){
            conn.avaliable=1;
        }
    }
    
    return conn;
}

Resource get_resource(const char* url){
    char pack[1024]={0};
    char buf[1024]={0};
    char redirect[256]={0},new_url[256]={0},old_url[256]={0};
    static int redirect_count=0;
    char* i;
    char* j;
    char* z;
    Resource res;
    
    Connection conn=open_url(url);
    if(!conn.avaliable){
        return res;
    }
    sprintf(pack,"GET %s HTTP/1.1\nHost: %s\nAccept: */*\nReferer: http://%s\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\nPragma: no-cache\nCache-Control: no-cache\nConnection: close\n\n",conn.url_info.file,conn.url_info.host_name,conn.url_info.host_name);
    send(conn.sock,pack,strlen(pack),0);
    recv(conn.sock,buf,sizeof(buf),0);
    //printf("%s\n",buf);
    if(strstr(buf,"HTTP/1.1 404")!=NULL || strstr(buf,"HTTP/1.0 404")!=NULL){
       return res;
    }
    i=(char *)strstr(buf,"Location:");
    if(i!=NULL && redirect_count<5){
        sscanf(i,"Location: %s",redirect);
        sprintf(old_url,"%s://%s:%d%s",conn.url_info.schema,conn.url_info.host_name,conn.url_info.port,conn.url_info.file);
        join_url(old_url,redirect,new_url);
        //printf("@#%s\n",new_url);
        redirect_count++;
        return get_resource(new_url);
    }
    i=(char *)strstr(buf,"Content-Length:");
    if(i!=NULL){
        sscanf(i,"Content-Length: %d",&res.file_size);
    }
    strcpy(res.file_url,url);
    //printf("#%d\n",res.file_size);
    for(z=(char*)url;(j=strstr(z,"/"))!=NULL;){
        z=j+sizeof(char);
    }
    strcpy(res.file_name,z);
    close(conn.sock);
    return res;
}

void* download_part(void * args)
{
    ThreadArg* targ=(ThreadArg*)args;
    Connection conn;
    FILE* f=NULL;
    char pack[1024]={0};
    char buf[1024]={0};
    int i=0,ct=0;
    char* body=NULL;
    //printf("%s,%d-%d\n",targ->res->file_url, targ->start_pos,targ->limit);
    conn=open_url(targ->res->file_url);
    while(!conn.avaliable){
        sleep(1);
        conn=open_url(targ->res->file_url);
    }
    if(conn.avaliable){

        f=fopen(targ->res->file_name,"rb+");
        fseek(f,targ->start_pos,0);
        sprintf(pack,"GET %s HTTP/1.1\nHost: %s\nAccept: */*\nReferer: http://%s\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\nRange: bytes=%d-%d\nPragma: no-cache\nCache-Control: no-cache\n\n",conn.url_info.file,conn.url_info.host_name,conn.url_info.host_name,targ->start_pos,targ->start_pos+targ->limit-1);
        //printf("%s",pack);
begin_down:
        send(conn.sock,pack,strlen(pack),0);
        i=recv(conn.sock,buf,sizeof(buf),0);
        
        if(strstr(buf,"HTTP/1.1 206")==NULL && strstr(buf,"HTTP/1.0 206")==NULL && strstr(buf,"HTTP/1.1 200")==NULL && strstr(buf,"HTTP/1.0 200")==NULL){
            sleep(2);
            memset(buf,0,sizeof(buf));
            conn=open_url(targ->res->file_url);
            goto begin_down;
        }
        //printf("##%s\n",body);
        body=strstr(buf,"\r\n\r\n")+4;
        if(body!=NULL){
            i=i-(body-buf);
            fwrite(body,sizeof(char),i,f);
            //printf("@@@@%x\n",buf);
            fflush(f);
            ct+=i;
            pthread_mutex_lock(&g_mut);
            g_downloaded+=i;
            pthread_mutex_unlock(&g_mut);
            
            while(ct< targ->limit){
                i=recv(conn.sock,buf,sizeof(buf),0);
                if(i==0){
                    fclose(f);
                    conn.avaliable=0;
                    while(!conn.avaliable){
                        sleep(2);
                        //printf("waiting...\n");
                        conn=open_url(targ->res->file_url);
                    }
                    memset(pack,0,sizeof(pack));
                    memset(buf,0,sizeof(buf));
                    sprintf(pack,"GET %s HTTP/1.1\nHost: %s\nAccept: */*\nReferer: http://%s\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.00; Windows 98)\nRange: bytes=%d-%d\nPragma: no-cache\nCache-Control: no-cache\n\n",conn.url_info.file,conn.url_info.host_name,conn.url_info.host_name,targ->start_pos+ct,targ->start_pos+targ->limit-1);
                    f=fopen(targ->res->file_name,"rb+");
                    fseek(f,targ->start_pos+ct,0);
                    goto begin_down;
                }
                
                fwrite(buf,sizeof(char),i,f);
                fflush(f);
                ct+=i;
                pthread_mutex_lock(&g_mut);
                g_downloaded+=i;
                g_breakpoint.tasks[targ->no][0]=targ->start_pos+ct;
                g_breakpoint.tasks[targ->no][1]=targ->limit-ct;
                g_breakpoint.downloaded=g_downloaded;
                pthread_mutex_unlock(&g_mut);
            }
            fclose(f);
            g_breakpoint.downloaded=g_downloaded;
            close(conn.sock);
        }
    }
    pthread_exit(NULL);
}
void* monitor(void* args){
    float p;
    int i,j,z,old;
    FILE* f;
    char cfgName[256];
    strcpy(cfgName,(char*)args);
    strcat(cfgName,".cfg");
    
    while(1){
        p=g_downloaded/(g_total+0.0);
        if(g_downloaded>=g_total)
                break;
        i=p*100/10;
        if(old!=g_downloaded){
            

            printf("\r");
            for(j=0;j<i;j++){
                printf("==");
            }
            printf("%2.0f%%",p*100);
            fflush(stdout);
        
            store_breakpoint(cfgName);
            old=g_downloaded;
        }
    }
    printf("\r====================100%%\n");
    remove(cfgName);
    pthread_exit(NULL);
}


void download(const char* url,int thread_amount,const char* file_name)
{
    ThreadArg targs[MAX_THREAD];
    pthread_attr_t * thAttr = NULL;
    pthread_t tids[MAX_THREAD],monitor_id,controler_id;
    Resource res;
    int i,block_size,t_start_pos,t_limit;
    FILE* f;
    char cfgName[256]={0};
    
    if(thread_amount>MAX_THREAD)
        return;
    res=get_resource(url);
    
    if(!strcmp(res.file_url,""))
        return;
    
    if(strcmp(file_name,""))
        strcpy(res.file_name,file_name);
    
    if(!strcmp(res.file_name,""))
        strcpy(res.file_name,"default_down");
    
    if(res.file_size<1000000)
        thread_amount=1;
    
    block_size=res.file_size/thread_amount;
    pthread_mutex_init(&g_mut,NULL);
    
    strcpy(cfgName,res.file_name);
    strcat(cfgName,".cfg");
    printf("downloading %s,%d bytes... \n",res.file_name,res.file_size);
    
    if(fopen(cfgName,"r")==NULL){
new_task:       
        f=fopen(res.file_name,"wb");
        if(f==NULL){
            strcpy(res.file_name,"default_down");
            f=fopen(res.file_name,"wb");
        }
        fclose(f);
        g_total=res.file_size;

        for(i=0;i<thread_amount;i++){
            targs[i].res=&res;
            targs[i].start_pos=block_size*i;
            targs[i].limit=block_size;
            if(i==thread_amount-1)
                targs[i].limit+= (res.file_size%thread_amount);
            
            targs[i].no=i;
            g_breakpoint.tasks[i][0]=targs[i].start_pos;
            g_breakpoint.tasks[i][1]=block_size;
            pthread_create(&tids[i], thAttr, download_part, (void *)&targs[i]);
        }
        
    }
    else{
        f=fopen(cfgName,"r");
        if(fscanf(f,"%d",&g_downloaded)==-1)
            goto new_task;
        //printf("#%d\n",g_downloaded);
        g_total=res.file_size;
        fscanf(f,"%d",&thread_amount);
        for(i=0;i<thread_amount;i++){
            fscanf(f,"%d-%d",&t_start_pos,&t_limit);
            targs[i].res=&res;
            targs[i].start_pos=t_start_pos;
            targs[i].limit=t_limit;
            targs[i].no=i;
            g_breakpoint.tasks[i][0]=targs[i].start_pos;
            g_breakpoint.tasks[i][1]=t_limit;
            pthread_create(&tids[i], thAttr, download_part, (void *)&targs[i]);
        }
        fclose(f);
    }
    
    pthread_create(&monitor_id,NULL,monitor,(void *)res.file_name);
    g_breakpoint.thread_amount=thread_amount;
    g_breakpoint.downloaded=g_downloaded;
    //printf("#%d\n",g_downloaded);
    /*for(i=0;i<thread_amount;i++){
        pthread_join(tids[i],NULL);
    }*/

    pthread_join(monitor_id,NULL);
}



int main (int ac, char * av[])
{
  int thread_amount=5;
  char file_name[256]={0};
  if(ac<2){
        printf("usage: qdown URL [thread_amount] [save as]\n");
        printf("example: qdown http://www.baidu.com/img/logo.gif 5 /home/sunjoy/log.gif\n");
  }
  else{
        if(ac>=3)
            sscanf(av[2],"%d",&thread_amount);
        if(ac>=4){
            strcpy(file_name,av[3]);
        }
        download(av[1],thread_amount,file_name);
        
  }
  
  return 0;
}




分享到:
评论
2 楼 dying 2011-09-03  
谢谢分享,不过写代码没注释的习惯真差
1 楼 zkf53064 2011-08-26  

相关推荐

    c语言多进程多线程编程.pdf

    《C语言多进程多线程编程》是一本深入探讨C语言在并发编程领域的专业书籍。在计算机科学中,进程和线程是操作系统中并行执行任务的基本单位,理解和掌握它们对于提升程序性能和优化资源利用至关重要。这本书籍针对...

    WIN10_VS2019_配置_多线程_C语言.docx

    在Windows 10环境下使用Visual Studio 2019(VS2019)进行C语言的多线程编程,需要进行一系列配置步骤。这里主要介绍如何配置VS2019以便支持C语言的多线程功能,以及解决在配置过程中可能遇到的问题。 首先,我们...

    C语言写的文本编辑器详细介绍点击进入更多下载

    9. **多线程**:高级的编辑器可能会使用多线程来实现如查找替换、自动保存等功能的并发执行。 10. **用户界面**:虽然C语言本身不支持图形用户界面(GUI),但可以通过调用操作系统提供的库(如Windows API或GTK+)...

    VC写的小巧多线程下载工具30K哦,win32wget, 附代码

    标题中的“VC写的小巧多线程下载工具30K哦,win32wget”指的是一个使用Microsoft Visual C++(简称VC)编写的轻量级下载程序,该程序只有大约30KB的大小,实现了多线程下载功能,并且其灵感或设计可能来源于Linux下...

    C语言写的命令行,比较漂亮

    在描述中提到“写个几个命令行”,这可能是指包含多个功能的命令行工具,每个功能对应一个不同的命令。开发者鼓励有兴趣的人下载并研究这些代码,这通常意味着源代码可能是开放的,以便学习和改进。 C语言在命令行...

    最新C语言电子书打包下载【8本】

    10. **并发编程**:进程与线程的概念,信号处理,共享内存,互斥锁等多线程编程技术。 11. **编译与链接**:了解编译器的工作原理,编译过程,链接器的作用,静态库与动态库的区别。 12. **调试技巧**:使用GDB进行...

    从千千静听下载歌词C语言实现

    在实际开发中,可能还需要考虑错误处理、多线程下载优化、缓存策略等问题。对于跨语言支持,需要处理不同字符集的编码问题,如UTF-8、GBK等。同时,考虑到日文和韩文歌词可能包含非ASCII字符,确保正确处理字符编码...

    C语言项目开发

    这本书可能涵盖了一些高级主题,如多线程编程、网络编程、系统调用等,这些都是大型项目中常见的技术。 总之,C语言项目开发不仅要求掌握语言本身,还需要了解如何将这些知识应用到实际问题中,这需要不断的实践和...

    POSIX多线程程序设计

    除此之外,书中还讨论了如何使用barrier、读/写锁、工作队列管理器等同步工具,并提供了大量实例和建议,帮助读者避免多线程编程中的错误和性能问题。 线程是实现某种功能的基本软件单元,与传统的进程相比,线程更...

    c语言实现歌词社区

    总的来说,"c语言实现歌词社区"项目是一个综合性的实践案例,涵盖了C语言程序设计的多个方面,包括数据结构、文件操作、数据库交互、用户界面设计、多线程、内存管理和安全性等多个核心知识点。对于初学者而言,这是...

    c语言实现的超级玛丽游戏源码用c语言写的超级玛丽游戏源代码.zip

    "有兴趣的可以下载研究"表明这个项目适合那些对游戏开发、C语言编程或者想学习如何构建类似游戏机制的人。"内部含有游戏材料资源"意味着源代码中可能包含了游戏所需的图像、音频和关卡设计等素材,这为分析和学习...

    多线程下载

    在iOS开发中,多线程下载是一项常见的技术需求,它能显著提高文件下载的效率,尤其是在处理大文件或网络环境不稳定时。标题“多线程下载”表明我们要讨论的是如何在iOS平台上实现利用多线程技术来加速文件的下载过程...

    c语言项目开发实例

    7. **客户端和服务器通信**:这部分内容可能涉及到网络编程,如TCP/IP套接字编程,数据的打包和解包,以及多线程或异步处理。理解网络通信协议和数据传输是关键。 8. **潜艇大战游戏**:这是一个基于网络的多人游戏...

    这是一个用C语言编写Linux平台的http文件下载器, 以命令行的方式运行.zip

    为了提高下载效率,下载器可能采用多线程或异步I/O技术,使得多个文件可以同时下载。线程的创建和同步、异步I/O的使用(如select、poll、epoll)都是需要考虑的技术点。 综上所述,这个C语言编写的Linux命令行HTTP...

    c语言项目源码大全.zip

    开发者可以通过编写游戏代码来学习多线程编程、时间控制、用户交互等高级主题。 在压缩包中的"文件名称列表"未提供具体项目名称,但根据一般项目分类,可能会包含以下几类: 1. 基础练习:如实现计算器、简单的...

    完整的标准C语言教材

    更深入的话题包括位运算、文件操作、多线程、网络编程等,这些在特定领域如系统编程、网络应用开发中发挥着关键作用。 总的来说,《完整的标准C语言教材》涵盖了C语言的所有核心概念和技巧,无论你是初学者还是有...

    pthread多线程c++动库下载

    这个“pthread多线程c++动库下载”压缩包包含了在Visual Studio(VS)环境下使用pthread所需的所有组件。 压缩包中的内容分为几个部分: 1. **Pre-built.2**:这个文件夹包含预编译好的库文件,适合不同的操作系统...

    C语言与MATLAB接口

    7. **并行计算**:MATLAB支持多线程和并行计算,这可以通过MATLAB引擎在C程序中实现。利用MATLAB的并行计算工具箱,C程序可以执行大规模的并行任务。 8. **优化和性能**:在C语言中调用MATLAB引擎可能会引入一定的...

    你必须知道的495个C语言问题(完整版本).rar

    14. **并发与多线程**:虽然不是C语言的标准特性,但通过POSIX线程库(pthreads)或其他平台特定的API,可以实现多线程编程。 15. **C99和C11标准**:了解C语言的新特性,如变量长度数组、匿名结构体和联合体、指定...

    C语言从入门到精通

    - 多线程编程:简要介绍多线程编程的基础,如何在C语言中创建和管理线程。 - 处理异常:讨论C语言中错误处理的一般方法,包括异常情况的处理和恢复策略。 8. 应用实例 - 实际项目案例:通过实际的编程项目案例来...

Global site tag (gtag.js) - Google Analytics