zoukankan      html  css  js  c++  java
  • 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的支持


       1/*
       2. ** description:qdown is a multithread downloader 
       3. ** author:Sunjoy 
       4. ** email:fxsjy @@@ yahoo.com.cn 
       5. ** from:ICT.CAS. 
       6. ** date:2007-9-10 
       7. ** 
       8. 
    */  
       
    9.   
      
    10. #include <stdio.h>  
      
    11. #include <stdlib.h>  
      
    12. #include <unistd.h>  
      
    13. #include <string.h>  
      
    14. #include <pthread.h>  
      
    15. #include <sys/types.h>  
      
    16. #include <sys/socket.h>  
      
    17. #include <netinet/in.h>  
      
    18. #include <arpa/inet.h>  
      
    19. #include <netdb.h>  
      
    20#define MAX_THREAD 100  
      
    21.   
      
    22. typedef struct URLInfo  
      
    23. {  
      
    24.     char schema[8];  
      
    25.     char host[256];  
      
    26.     char host_name[256];  
      
    27.     unsigned int port;  
      
    28.     char file[256];  
      
    29. }URLInfo;  
      
    30.   
      
    31. typedef struct Connection  
      
    32. {  
      
    33.     int sock;  
      
    34.     URLInfo url_info;  
      
    35.     int avaliable;  
      
    36. }Connection;  
      
    37.   
      
    38. typedef struct Resource  
      
    39. {  
      
    40.     char file_url[256];  
      
    41.     int file_size;  
      
    42.     char file_name[256];  
      
    43. }Resource;  
      
    44.   
      
    45. typedef struct ThreadArg  
      
    46. {  
      
    47.     Resource* res;  
      
    48.     int start_pos;  
      
    49.     int limit;  
      
    50.     int no;  
      
    51. }ThreadArg;  
      
    52.   
      
    53. typedef struct BreakPoint  
      
    54. {  
      
    55.     int downloaded;  
      
    56.     int thread_amount;  
      
    57.     int tasks[MAX_THREAD][2];  
      
    58.       
      
    59. }BreakPoint;  
      
    60.   
      
    61. pthread_mutex_t g_mut;  
      
    62int g_total=0;  
      
    63int g_downloaded=0;  
      
    64. BreakPoint g_breakpoint;  
      
    65.   
      
    66. URLInfo parse_url(const char *url);  
      
    67. Connection open_url(const char * url);  
      
    68. Resource get_resource(const char *url);  
      
    69void join_url(const char* old_url,const char* redirect,char * new_url);  
      
    70void download(const char* url,int thread_amount,const char* file_name);  
      
    71void* download_part(void* args);  
      
    72void* monitor(void *args);  
      
    73void store_breakpoint(char * cfgName);  
      
    74.   
      
    75void store_breakpoint(char * cfgName)  
      
    76. {  
      
    77.     int z;  
      
    78.     FILE* f;  
      
    79.     f=fopen(cfgName,"w");  
      
    80.     fprintf(f,"%d\n",g_breakpoint.downloaded);  
      
    81.     fprintf(f,"%d\n",g_breakpoint.thread_amount);  
      
    82.     for(z=0;z<g_breakpoint.thread_amount;z++){  
      
    83.        fprintf(f,"%d-%d\n",g_breakpoint.tasks[z][0],g_breakpoint.tasks[z][1]);  
      
    84.     }  
      
    85.     fclose(f);  
      
    86. }  
      
    87.   
      
    88void join_url(const char* old_url,const char* redirect,char * new_url)  
      
    89. {  
      
    90.     char stack1[256][256]={0},stack2[256][256]={0};  
      
    91.     int i=0,j=0,p1=0,p2=0;  
      
    92.     char seg[256]={0};  
      
    93.     URLInfo temp_urlinfo;  
      
    94.       
      
    95.     memset(new_url,0,sizeof(new_url));  
      
    96.     if(strstr(redirect,"://")!=NULL){  
      
    97.         strcpy(new_url,redirect);  
      
    98.     }  
      
    99.     else{  
     
    100.         while(1){  
     
    101.             while(redirect[i]!='/' && redirect[i]!=0){  
     
    102.                 seg[j++]=redirect[i++];  
     
    103.             }      
     
    104.             strcpy(stack1[p1++],seg);  
     
    105.             memset(seg,0,sizeof(seg));  
     
    106.             j=0;  
     
    107.             if(redirect[i]==0)  
     
    108.                 break;  
     
    109.             i++;  
     
    110.         }  
     
    111.         for(i=0;i<p1;i++){  
     
    112.             if(!strcmp(stack1[i],".."&& p2>-1)  
     
    113.                 p2--;  
     
    114.             else if(strcmp(stack1[i],".")){  
     
    115.                 strcpy(stack2[p2++],stack1[i]);  
     
    116.             }  
     
    117.         }  
     
    118.         //printf("##%s\n",stack2[0]);  
     119.      
     
    120.         if(!strcmp(stack2[0],"")){  
     
    121.             temp_urlinfo=parse_url(old_url);  
     
    122.             sprintf(new_url,"%s://%s:%d/",temp_urlinfo.schema,temp_urlinfo.host,temp_urlinfo.port);            
     
    123.         }  
     
    124.         else{  
     
    125.             i=strlen(old_url)-1;  
     
    126.             while(old_url[i]!='/')  
     
    127.                 i--;  
     
    128.             //printf("##%c\n",old_url[i]);  
     129.             strncpy(new_url,old_url,i+1);  
     
    130.             new_url[i+1]=0;  
     
    131.         }  
     
    132.         //printf("##%s\n",new_url);  
     133.         for(j=0;j<p2-1;j++){  
     
    134.             strcat(new_url,stack2[j]);  
     
    135.             strcat(new_url,"/");  
     
    136.         }  
     
    137.         strcat(new_url,stack2[p2-1]);  
     
    138.     }  
     
    139. }  
     
    140.   
     
    141. URLInfo parse_url(const char* url){  
     
    142.     int i=0,j=0;  
     
    143.     char schema[8]={0};  
     
    144.     char host[256]={0};  
     
    145.     char port[8]={0};  
     
    146.     char file[256]={0};  
     
    147.     char IP[32]={0};  
     
    148.     URLInfo url_info;  
     
    149.     struct hostent* hptr;  
     
    150.       
     
    151.     while(url[i]!=':'){  
     
    152.         schema[j++]=url[i++];  
     
    153.     }  
     
    154.   
     
    155.     for(i+=3,j=0;url[i]!=':' && url[i]!='/' && url[i]!=0;){  
     
    156.         host[j++]=url[i++];  
     
    157.     }  
     
    158.       
     
    159.     if(url[i]==':'){  
     
    160.         for(i+=1,j=0;url[i]!='/';){  
     
    161.             port[j++]=url[i++];  
     
    162.         }  
     
    163.         sscanf(port,"%d",&url_info.port);  
     
    164.     }  
     
    165.     else{  
     
    166.         url_info.port=80;  
     
    167.     }  
     
    168.       
     
    169.     if(url[i]!=0){  
     
    170.         for(j=0;url[i]!=0;){  
     
    171.             file[j++]=url[i++];  
     
    172.         }  
     
    173.     }  
     
    174.     else{  
     
    175.         file[0]='/';  
     
    176.     }  
     
    177.       
     
    178.     strcpy(url_info.schema,schema);  
     
    179.     strcpy(url_info.file,file);  
     
    180.     strcpy(url_info.host_name,host);  
     
    181.     hptr=gethostbyname(host);  
     
    182.   
     
    183.      
     
    184.     if(hptr!=NULL){  
     
    185.         strcpy(url_info.host,  
     
    186.             inet_ntop(hptr->h_addrtype,*(hptr->h_addr_list),IP,sizeof(IP))  
     
    187.         );  
     
    188.     }  
     
    189.     //printf("%s\n",url_info.host);  
     190.     return url_info;  
     
    191. }  
     
    192. Connection open_url(const char* url){  
     
    193.     Connection conn;  
     
    194.     struct sockaddr_in remote_addr,local_addr;  
     
    195.   
     
    196.     conn.avaliable=0;  
     
    197.     conn.url_info=parse_url(url);  
     
    198.       
     
    199.     local_addr.sin_family=AF_INET;  
     
    200.     local_addr.sin_addr.s_addr=htonl(INADDR_ANY);  
     
    201.     local_addr.sin_port=htons(0);  
     
    202.     remote_addr.sin_family=AF_INET;  
     
    203.     remote_addr.sin_addr.s_addr=inet_addr(conn.url_info.host);  
     
    204.     remote_addr.sin_port=htons(conn.url_info.port);  
     
    205.       
     
    206.     conn.sock=socket(AF_INET,SOCK_STREAM,0);  
     
    207.     if(bind(conn.sock,  
     
    208.         (struct sockaddr*)&local_addr,  
     
    209.         sizeof(local_addr))<0){  
     
    210.             printf("bind error\n");  
     
    211.     }  
     
    212.       
     
    213.       
     
    214.       
     
    215.     if(conn.sock){  
     
    216.         if(  
     
    217.             connect(conn.sock,(struct sockaddr*)&remote_addr,sizeof(remote_addr))!=-1  
     
    218.         ){  
     
    219.             conn.avaliable=1;  
     
    220.         }  
     
    221.     }  
     
    222.       
     
    223.     return conn;  
     
    224. }  
     
    225.   
     
    226. Resource get_resource(const char* url){  
     
    227.     char pack[1024]={0};  
     
    228.     char buf[1024]={0};  
     
    229.     char redirect[256]={0},new_url[256]={0},old_url[256]={0};  
     
    230.     static int redirect_count=0;  
     
    231.     char* i;  
     
    232.     char* j;  
     
    233.     char* z;  
     
    234.     Resource res;  
     
    235.       
     
    236.     Connection conn=open_url(url);  
     
    237.     if(!conn.avaliable){  
     
    238.         return res;  
     
    239.     }  
     
    240.     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);  
     
    241.     send(conn.sock,pack,strlen(pack),0);  
     
    242.     recv(conn.sock,buf,sizeof(buf),0);  
     
    243.     //printf("%s\n",buf);  
     244.     if(strstr(buf,"HTTP/1.1 404")!=NULL || strstr(buf,"HTTP/1.0 404")!=NULL){  
     
    245.        return res;  
     
    246.     }  
     
    247.     i=(char *)strstr(buf,"Location:");  
     
    248.     if(i!=NULL && redirect_count<5){  
     
    249.         sscanf(i,"Location: %s",redirect);  
     
    250.         sprintf(old_url,"%s://%s:%d%s",conn.url_info.schema,conn.url_info.host_name,conn.url_info.port,conn.url_info.file);  
     
    251.         join_url(old_url,redirect,new_url);  
     
    252.         //printf("@#%s\n",new_url);  
     253.         redirect_count++;  
     
    254.         return get_resource(new_url);  
     
    255.     }  
     
    256.     i=(char *)strstr(buf,"Content-Length:");  
     
    257.     if(i!=NULL){  
     
    258.         sscanf(i,"Content-Length: %d",&res.file_size);  
     
    259.     }  
     
    260.     strcpy(res.file_url,url);  
     
    261.     //printf("#%d\n",res.file_size);  
     262.     for(z=(char*)url;(j=strstr(z,"/"))!=NULL;){  
     
    263.         z=j+sizeof(char);  
     
    264.     }  
     
    265.     strcpy(res.file_name,z);  
     
    266.     close(conn.sock);  
     
    267.     return res;  
     
    268. }  
     
    269.   
     
    270void* download_part(void * args)  
     
    271. {  
     
    272.     ThreadArg* targ=(ThreadArg*)args;  
     
    273.     Connection conn;  
     
    274.     FILE* f=NULL;  
     
    275.     char pack[1024]={0};  
     
    276.     char buf[1024]={0};  
     
    277.     int i=0,ct=0;  
     
    278.     char* body=NULL;  
     
    279.     //printf("%s,%d-%d\n",targ->res->file_url, targ->start_pos,targ->limit);  
     280.     conn=open_url(targ->res->file_url);  
     
    281.     while(!conn.avaliable){  
     
    282.         sleep(1);  
     
    283.         conn=open_url(targ->res->file_url);  
     
    284.     }  
     
    285.     if(conn.avaliable){  
     
    286.   
     
    287.         f=fopen(targ->res->file_name,"rb+");  
     
    288.         fseek(f,targ->start_pos,0);  
     
    289.         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);  
     
    290.         //printf("%s",pack);  
     291. begin_down:  
     
    292.         send(conn.sock,pack,strlen(pack),0);  
     
    293.         i=recv(conn.sock,buf,sizeof(buf),0);  
     
    294.           
     
    295.         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){  
     
    296.             sleep(2);  
     
    297.             memset(buf,0,sizeof(buf));  
     
    298.             conn=open_url(targ->res->file_url);  
     
    299.             goto begin_down;  
     
    300.         }  
     
    301.         //printf("##%s\n",body);  
     302.         body=strstr(buf,"\r\n\r\n")+4;  
     
    303.         if(body!=NULL){  
     
    304.             i=i-(body-buf);  
     
    305.             fwrite(body,sizeof(char),i,f);  
     
    306.             //printf("@@@@%x\n",buf);  
     307.             fflush(f);  
     
    308.             ct+=i;  
     
    309.             pthread_mutex_lock(&g_mut);  
     
    310.             g_downloaded+=i;  
     
    311.             pthread_mutex_unlock(&g_mut);  
     
    312.               
     
    313.             while(ct< targ->limit){  
     
    314.                 i=recv(conn.sock,buf,sizeof(buf),0);  
     
    315.                 if(i==0){  
     
    316.                     fclose(f);  
     
    317.                     conn.avaliable=0;  
     
    318.                     while(!conn.avaliable){  
     
    319.                         sleep(2);  
     
    320.                         //printf("waiting\n");  
     321.                         conn=open_url(targ->res->file_url);  
     
    322.                     }  
     
    323.                     memset(pack,0,sizeof(pack));  
     
    324.                     memset(buf,0,sizeof(buf));  
     
    325.                     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);  
     
    326.                     f=fopen(targ->res->file_name,"rb+");  
     
    327.                     fseek(f,targ->start_pos+ct,0);  
     
    328.                     goto begin_down;  
     
    329.                 }  
     
    330.                   
     
    331.                 fwrite(buf,sizeof(char),i,f);  
     
    332.                 fflush(f);  
     
    333.                 ct+=i;  
     
    334.                 pthread_mutex_lock(&g_mut);  
     
    335.                 g_downloaded+=i;  
     
    336.                 g_breakpoint.tasks[targ->no][0]=targ->start_pos+ct;  
     
    337.                 g_breakpoint.tasks[targ->no][1]=targ->limit-ct;  
     
    338.                 g_breakpoint.downloaded=g_downloaded;  
     
    339.                 pthread_mutex_unlock(&g_mut);  
     
    340.             }  
     
    341.             fclose(f);  
     
    342.             g_breakpoint.downloaded=g_downloaded;  
     
    343.             close(conn.sock);  
     
    344.         }  
     
    345.     }  
     
    346.     pthread_exit(NULL);  
     
    347. }  
     
    348void* monitor(void* args){  
     
    349.     float p;  
     
    350.     int i,j,z,old;  
     
    351.     FILE* f;  
     
    352.     char cfgName[256];  
     
    353.     strcpy(cfgName,(char*)args);  
     
    354.     strcat(cfgName,".cfg");  
     
    355.       
     
    356.     while(1){  
     
    357.         p=g_downloaded/(g_total+0.0);  
     
    358.         if(g_downloaded>=g_total)  
     
    359.                 break;  
     
    360.         i=p*100/10;  
     
    361.         if(old!=g_downloaded){  
     
    362.               
     
    363.   
     
    364.             printf("\r");  
     
    365.             for(j=0;j<i;j++){  
     
    366.                 printf("==");  
     
    367.             }  
     
    368.             printf("%2.0f%%",p*100);  
     
    369.             fflush(stdout);  
     
    370.           
     
    371.             store_breakpoint(cfgName);  
     
    372.             old=g_downloaded;  
     
    373.         }  
     
    374.     }  
     
    375.     printf("\r====================100%%\n");  
     
    376.     remove(cfgName);  
     
    377.     pthread_exit(NULL);  
     
    378. }  
     
    379.   
     
    380.   
     
    381void download(const char* url,int thread_amount,const char* file_name)  
     
    382. {  
     
    383.     ThreadArg targs[MAX_THREAD];  
     
    384.     pthread_attr_t * thAttr = NULL;  
     
    385.     pthread_t tids[MAX_THREAD],monitor_id,controler_id;  
     
    386.     Resource res;  
     
    387.     int i,block_size,t_start_pos,t_limit;  
     
    388.     FILE* f;  
     
    389.     char cfgName[256]={0};  
     
    390.       
     
    391.     if(thread_amount>MAX_THREAD)  
     
    392.         return;  
     
    393.     res=get_resource(url);  
     
    394.       
     
    395.     if(!strcmp(res.file_url,""))  
     
    396.         return;  
     
    397.       
     
    398.     if(strcmp(file_name,""))  
     
    399.         strcpy(res.file_name,file_name);  
     
    400.       
     
    401.     if(!strcmp(res.file_name,""))  
     
    402.         strcpy(res.file_name,"default_down");  
     
    403.       
     
    404.     if(res.file_size<1000000)  
     
    405.         thread_amount=1;  
     
    406.       
     
    407.     block_size=res.file_size/thread_amount;  
     
    408.     pthread_mutex_init(&g_mut,NULL);  
     
    409.       
     
    410.     strcpy(cfgName,res.file_name);  
     
    411.     strcat(cfgName,".cfg");  
     
    412.     printf("downloading %s,%d bytes \n",res.file_name,res.file_size);  
     
    413.       
     
    414.     if(fopen(cfgName,"r")==NULL){  
     
    415. new_task:         
     
    416.         f=fopen(res.file_name,"wb");  
     
    417.         if(f==NULL){  
     
    418.             strcpy(res.file_name,"default_down");  
     
    419.             f=fopen(res.file_name,"wb");  
     
    420.         }  
     
    421.         fclose(f);  
     
    422.         g_total=res.file_size;  
     
    423.   
     
    424.         for(i=0;i<thread_amount;i++){  
     
    425.             targs[i].res=&res;  
     
    426.             targs[i].start_pos=block_size*i;  
     
    427.             targs[i].limit=block_size;  
     
    428.             if(i==thread_amount-1)  
     
    429.                 targs[i].limit+= (res.file_size%thread_amount);  
     
    430.               
     
    431.             targs[i].no=i;  
     
    432.             g_breakpoint.tasks[i][0]=targs[i].start_pos;  
     
    433.             g_breakpoint.tasks[i][1]=block_size;  
     
    434.             pthread_create(&tids[i], thAttr, download_part, (void *)&targs[i]);  
     
    435.         }  
     
    436.           
     
    437.     }  
     
    438.     else{  
     
    439.         f=fopen(cfgName,"r");  
     
    440.         if(fscanf(f,"%d",&g_downloaded)==-1)  
     
    441.             goto new_task;  
     
    442.         //printf("#%d\n",g_downloaded);  
     443.         g_total=res.file_size;  
     
    444.         fscanf(f,"%d",&thread_amount);  
     
    445.         for(i=0;i<thread_amount;i++){  
     
    446.             fscanf(f,"%d-%d",&t_start_pos,&t_limit);  
     
    447.             targs[i].res=&res;  
     
    448.             targs[i].start_pos=t_start_pos;  
     
    449.             targs[i].limit=t_limit;  
     
    450.             targs[i].no=i;  
     
    451.             g_breakpoint.tasks[i][0]=targs[i].start_pos;  
     
    452.             g_breakpoint.tasks[i][1]=t_limit;  
     
    453.             pthread_create(&tids[i], thAttr, download_part, (void *)&targs[i]);  
     
    454.         }  
     
    455.         fclose(f);  
     
    456.     }  
     
    457.       
     
    458.     pthread_create(&monitor_id,NULL,monitor,(void *)res.file_name);  
     
    459.     g_breakpoint.thread_amount=thread_amount;  
     
    460.     g_breakpoint.downloaded=g_downloaded;  
     
    461.     //printf("#%d\n",g_downloaded);  
     462.     /*for(i=0;i<thread_amount;i++){  
     463.         pthread_join(tids[i],NULL);  
     464.     }
    */  
     
    465.   
     
    466.     pthread_join(monitor_id,NULL);  
     
    467. }  
     
    468.   
     
    469.   
     
    470.   
     
    471int main (int ac, char * av[])  
     
    472. {  
     
    473.   int thread_amount=5;  
     
    474.   char file_name[256]={0};  
     
    475.   if(ac<2){  
     
    476.         printf("usage: qdown URL [thread_amount] [save as]\n");  
     
    477.         printf("example: qdown http://www.baidu.com/img/logo.gif 5 /home/sunjoy/log.gif\n");  
     
    478.   }  
     
    479.   else{  
     
    480.         if(ac>=3)  
     
    481.             sscanf(av[2],"%d",&thread_amount);  
     
    482.         if(ac>=4){  
     
    483.             strcpy(file_name,av[3]);  
     
    484.         }  
     
    485.         download(av[1],thread_amount,file_name);  
     
    486.           
     
    487.   }  
     
    488.     
     
    489.   return 0;  
     
    490. }  
  • 相关阅读:
    【转】C#中的委托和事件
    ASP.NET上传文件实例代码
    我在配置OpenCV环境以及使用VS2013运行代码时遇到的问题
    JAVA_HOME环境变量失效的解决办法
    一道int和unsigned char之间强制类型转换的题目
    为什么不看好OpenStack:它的没落不可避免[转]
    一个销前谈云桌面的适用场景(再议云桌面营销之选择正确的客户环境)
    云桌面的前世今生
    云桌面三大谎言之GPU虚拟化
    如何理解清零心态
  • 原文地址:https://www.cnblogs.com/Safe3/p/1452901.html
Copyright © 2011-2022 走看看