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. }  
posted @ 2009-05-08 21:52  有安科技  阅读(2224)  评论(0编辑  收藏  举报