犀利小猫

日积月累 滴水石穿

导航

linux下http协议 多线程下载实现

该程序只能在http协议下工作,等以后研究其他协议时再补充。  

(网络服务器用的是腾讯公司的linuxqq 希望他们能原谅我……)
编程思路:  
1、分析http协议 数据包。  
以下是一个请求报文与相应的回复报文的例子  

GET /linuxqq/linuxqq-v1.0.2-beta1.i386.rpm HTTP/1.1  
Accept: */*  
Accept-Language: en-us  
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)  
Host: :80  
Connection: Keep-Alive  

HTTP/1.1 206 Partial Content  
Content-Length: 1009349  
Content-Range: bytes 0-1009348/5046743  
Server: qqdlsrv(1.84 for linux)  
Connection: Keep-alive  
Content-Disposition: attachment; filename=linuxqq-v1.0.2-beta1.i386.rpm  
Accept-Ranges: bytes  
Content-Type: application/octet-stream  

2、取得文件大小,然后进行对文件的分块。  
3、对文件分块下载  
4、合并文件,并改名。

总结:这个分块再合并总感觉太过于啰嗦。  
是否还有另外一种方法,  
如迅雷,在一开始就取得文件大小就在硬盘分配空间,  
然后分块下载直接写入到文件的不同块?  
是否可以用文件指针锁住不同的区域并写入?



/*  
* down.c  
*  
*  Created on: Mar 27, 2009  
*      Author: root  
* this program is to download file in http protocol with multthreading  
*/  
#ifndef STRUCT_H_  
#define STRUCT_H_  

#endif /* STRUCT_H_ */  
#include <unistd.h>  
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/socket.h>  
#include <sys/types.h>  
#include <netdb.h>  
#include <netinet/in.h>  
//create a struct to save the file part infomation  
struct filepart  
{  
int partnum;  
char pathname[128];  
long int lenth;  
long int datastar;  
long int dataend;  
struct filepart *next;  
};  
typedef struct filepart FilePart;  
struct hostent *host;  
char filepath[2048];  //save the filepath on the server  
char hostname[1024];  //save the name of the server  
char filename[256];  //save the name of the file  
char localpath[512];  
int port,part;  
long int FileLen;  //the file lenth  
void getname( char * ulr); //解析url,并从中取得主机名,ip地址 、文件路径以及端口  
void filepart( long int datalen,int part ); //对文件进行分块  
void *getfile( FilePart *pointer); //下载线程函数  
long int getFileLen( struct hostent *host  ); //获取http包头,分析文件大小  
void createthread( FilePart * head, int num );  //创建线程函数 参数为链表头指针和分块数目  
int mergefile( FilePart * head);  
int main(void)  
{  
char ulr[128];  
printf("please input the website:\n");  
scanf("%s",ulr);  
printf("the website is :%s\n",ulr);  
printf("input the direct to save file:\n");  
scanf("%s",localpath);  
getname( ulr );             //get the ipaddress of the host  
// getfile( host );  
FileLen = getFileLen( host );     //get the lenth of the file  
filepart( FileLen, 3);  

puts("!!!Hello World!!!"); /* prints !!!Hello World!!! */  
return EXIT_SUCCESS;  
}  



long int getFileLen( struct hostent *host  )  
{  
struct sockaddr_in sock_serv;  
int sockfd;  
char Request[1024];  
long int filelen;  
int recvbytes;  
char *buff ,*buffp,*Plen;  
buff = (char *)malloc( 1024 * sizeof(char));  
memset( buff, 0, sizeof(buff));  
sockfd = socket( AF_INET, SOCK_STREAM,0);  
// printf("sockfd = %d\n",sockfd);  
if(sockfd < 0)  
{  
  perror("socket");  
  exit(1);  
}  
memset( &sock_serv, 0, sizeof( sock_serv));  
sock_serv.sin_family = AF_INET;  
sock_serv.sin_port = htons(port);  
sock_serv.sin_addr = *((struct in_addr *)host -> h_addr);  
if(0>connect(sockfd,(struct sockaddr *)&sock_serv, sizeof(struct sockaddr)))  
{  
  perror("connect");  
  exit(1);  
}  
// printf("connect success!\n");  
printf("file path is:%s\n",filepath);  
sprintf(Request, "GET %s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: en-us\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\nHost: %s:%d\r\nConnection: Keep-Alive\r\n\r\n", filepath, hostname, port);  
// printf("%s",Request);  

//send the http protocl packet  
if( 0> send(sockfd, Request, strlen(Request), 0))  
{  
  perror("send");  
  exit(1);  
}  
while( 1 )  
{  
  recvbytes = recv(sockfd, buff, 1024,0);  
//  printf("recvbytes = %d",recvbytes);  
  if(0 > recvbytes )  
  {  
   perror("recv");  
   exit(1);  
  }  
  Plen = strstr(buff, "Length:");  
  if( NULL != Plen )  
  {  
   Plen = strchr( Plen, ':');  
   Plen++;  
   filelen = atol( Plen );  
   printf("the file data is %ld\n",filelen);  
  }  
  buffp = strstr(buff, "\r\n\r\n");  
//  printf("\n%s",buff);  
  if( NULL != buffp )  
   break;  
}  
free( buff ); buff = NULL;  
close(sockfd);  
return filelen;  
}  

void getname( char * ulr)  
{  
char head[10];          //save the"http://" or "https://"  
char hostname[1024];  
char *pointer;  
char *pointer2;  
char **addrs;  
pointer = strstr(ulr,"//");   //move the pointer to the "//" of the url  
pointer += 2;          //move the pointer to the right of "//" of the url  
strncpy(head, ulr, pointer - ulr);//copy "  
http://" to head  
// printf("head = %s\n",head);  
pointer2 = strchr( pointer,':'); //find the port of the server  
if( pointer2 == NULL)       //if there is no port  
{  
  char *temp = strchr(pointer,'/');       //move pointer to the first '/'  
  port = 80;  
  if( temp != NULL )  
  {  
   pointer2 = temp++;          //save a pointer pointo the right of the first '/'  
   strncpy(hostname, pointer, pointer2 - pointer);  //save the host name  
   strcpy(filepath, pointer2);            //the right of the first '/' are the filepath and name, then save!  
   printf("filepath:%s\n",filepath);  
   while( NULL != temp )  
   {  
    temp = strchr( temp,'/');            //find the right '/'  
    if( NULL != temp )  
    {  
     pointer2 = temp;  
     temp++;  
    }  
   }  
   strcpy(filename, ++pointer2);           //the right of the right '/' is the file name!  
   printf("filename is: %s\n",filename);  
          //between the first left '/' and the right '/' is the file path  
   printf("save to %s\n",localpath);  
  /* if( *temp == 0 )  
   {  
    strcpy(filepath,"/index.html");  
    printf("filepath:%s\n",filepath);  
   }  
  */  
  }  
  else  
  {  
   strcpy(hostname,pointer);  
   strcpy(filepath,"/index.html");  
   printf("filepath = %s\n",filepath);  
  }  
}  
else  
  {  
   strncpy(hostname, pointer, pointer2 - pointer);  
   port = atoi(++pointer2);  
   printf("port = %d\n",port);  
  }  
printf("hostname = %s\n",hostname);  
host = gethostbyname(hostname);  
if( host < 0)  
{  
  perror("gethostbyname");  
  exit(1);  
}  
addrs = host -> h_addr_list;  
printf("%s",inet_ntoa(*(struct in_addr *)*addrs));  
printf("\n");  
}  

void filepart( long int datalen,int part )      //move the file lenth and the number of cutting parts  
{  
long int partlen = datalen / part;  
long int end = datalen - 1;  
int i;  
// printf("partlen = %ld\nend = %ld\n",partlen,end);  
FilePart *head ,*present;  
head = (FilePart *)malloc(sizeof(FilePart));    //create the head of the link  
head -> partnum =  1;  
head -> datastar = 0;  
head -> dataend = partlen;  
head -> lenth = partlen;  
sprintf(head -> pathname,"%s/part%d",localpath,1);  
if(part == 1)  
  head -> dataend = end;  
present = head;  
for( i = 0; i < part - 1; i++)  
{  
  present ->next = (FilePart *)malloc(sizeof(FilePart));  
  memset(present->next, 0, sizeof(FilePart));  
  present ->next -> partnum = i+2;  
  present ->next -> datastar = present -> dataend +1;  
  if( i == (part-2))  
   {  
    present ->next -> dataend = end;  
    present ->next -> lenth = datalen - (part-1)*partlen;  
   }  
  else  
   {  
    present ->next -> dataend = partlen * (i+2);  
    present ->next -> lenth = partlen;  
   }  
  sprintf(present ->next->pathname,"%s/part%d",localpath,(i+2));  
  present = present->next;  
}  
createthread( head, part );  
mergefile( head );  
}  
void createthread( FilePart * head, int num )  
{  
// printf("start to create thread\n");  
int i = 1 ,res;  
long int pres[num];  
pthread_t thread[num];  
FilePart *present = head;  
for(i = 0; i < num; i++)  
{  
  res = pthread_create(&thread, NULL, getfile, present);  
  if(res != 0)  
  {  
   perror("thread creation failed");  
   exit(1);  
  }  
//  printf("start to create thread %d\n",i+1);  
  present = present -> next;  
}  
long int all = 0;  
for( i = 0; i < num; i++)  
{  
  res = pthread_join(thread,&pres);  
  if( res != 0)  
  {  
   perror("pthread join");  
   exit(1);  
  }  
  printf("thread %d finished!\n",i+1);  
  all += pres;  
}  
printf("all download:%ld!\n",all);  
}  
void *getfile( FilePart *pointer)  
{  
char buff[1024],request[2048];  
struct sockaddr_in sock_serv;  
FILE *fp,* fplog;  
int recvbytes,psockfd;  
long int download = 0;  
psockfd = socket(AF_INET, SOCK_STREAM, 0);  
if( psockfd < 0)  
{  
  perror("socket");  
  pthread_exit(1);  
}  
// printf("psockfd = %d\n",psockfd);  
memset( &sock_serv, 0, sizeof(sock_serv));  
sock_serv.sin_family = AF_INET;  
sock_serv.sin_port = htons(port);  
sock_serv.sin_addr = *((struct in_addr *)host -> h_addr);  
if( ( connect( psockfd, (struct sockaddr *)&sock_serv, sizeof(struct sockaddr) ) )< 0 )  
{  
  perror("connect");  
  pthread_exit(1);  
}  
// printf("connect success!\n");  
// printf("filepath is :%s\n",filepath);  
sprintf(request, "GET %s HTTP/1.1\r\nAccept: */*\r\nAccept-Language: en-us\r\nUser-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\r\nHost: %s:%d\r\nRange:bytes=%d-%d\r\nConnection: Keep-Alive\r\n\r\n",filepath, hostname, port,pointer ->datastar,pointer ->dataend);  
// printf("ready to send:\n %s",request);  
int sends = send( psockfd, request, strlen(request), 0);  
if( sends < 0)  
{  
  perror("send");  
  exit(1);  
}  
char filelog[32];  
sprintf(filelog,"/tmp/down/part%dlog",pointer-> partnum);  
fplog = fopen( filelog,"w+");  
fp = fopen( pointer->pathname,"w+");  
if(fp < 0 )  
{  
  perror("open ");  
  pthread_exit(1);  
}  
int  in = 0;  
do  
{  
  memset(buff, 0, sizeof(buff));  
  recvbytes = recv(psockfd,buff,1024,0);  
//  printf(" recv %d bytes!\n",recvbytes);  
  if( recvbytes == 0)  
   break;  
//  printf("recvbytes = %d\n",recvbytes);  
  if( recvbytes < 0)  
  {  
   perror("recv");  
   pthread_exit(1);  
  }  
  char *p;  
  p = strstr(buff,"\r\n\r\n");  

/* when recv the file stream,if there don't include http protocol head,skip,or discaerd the head,and save the other to the file */  
  if( !in )  
  {  
   if(  p != NULL )  
   {  
    p += 4;  
    in = fwrite(buff, 1, ( p - buff), fplog ); //discard the http protocol head  
//    printf("\nin = %d\n (p - buff)=%d\n",in,(p-buff));  
    recvbytes = recvbytes - ( p - buff );  
//    printf("\n recvbytes = %d\n",recvbytes);  
    in = fwrite( p, 1, recvbytes, fp);  
//    printf("in = %d\n",in);  
//    printf("%s",buff);  
    download += in;  
   }  
   else  
   {  
//    printf("%s",buff);  
//    fwrite(buff, 1, recvbytes, fp );  
   }  
  }  
  else  
  {  
   in = fwrite(buff,1, recvbytes, fp);  
//   printf("in = %d\n",in);  
   download += in;  
  }  
  //printf("\n");  
}while( recvbytes > 0 );  
printf("%d have downloaded!\n",download);  
fclose(fp);  
fclose(fplog);  
close(psockfd);  
pthread_exit(download);  
}  
/*merge all parts to one file and remove the temp file rename the file*/  
int mergefile( FilePart * head)  
{  
FilePart *present ,*release;  
char *buf = (char *)malloc(1024*sizeof(char));  
FILE *pfread,*pfwrite;  
int readnum,writenum;  
pfwrite = fopen(head->pathname,"ab");  //open the first part as append mode.  
if( pfwrite < 0)  
{  
  printf("open file");  
  exit(1);  
}  
present = head->next;  
while( present )  
{  
  pfread = fopen( present->pathname,"rb");  //open temp part's data  
  if(pfread < 0)  
  {  
   printf("fopen");  
   exit(1);  
  }  
  else  
   printf("open success!\n");  
  while( !feof(pfread))  
  {  
   readnum = fread( buf, sizeof(char), 1024, pfread); //read data from temp part's  
   writenum = fwrite( buf, sizeof(char), readnum, pfwrite);  //write to the first part  
//   printf("%ld have written!\n",writenum);  
  }  
  printf(" read %d part\n", present->partnum);  
  fclose(pfread);  //close the temp file  
  remove(present->pathname); //remove the temp file which have already read  
  release = present;  
  present = present->next;  
  free(release); //release the link  
}  
free(buf);buf=NULL;  
fclose(pfwrite);  
sprintf(localpath,"%s/%s",localpath,filename);  
if( 0 > (rename( head->pathname,localpath)))  
  perror("rename");  
free(head); //free the head of the link  
return 0;  
}  

运行环境 gcc + linux 2.6  
[root@Eric home]# ./a.out  
please input the website:  
http://dl_dir.qq.com/linuxqq/linuxqq-v1.0.2-beta1.i386.rpm  
the website is :http://dl_dir.qq.com/linuxqq/linuxqq-v1.0.2-beta1.i386.rpm  
input the direct to save file:  
/tmp/down  
filepath:/linuxqq/linuxqq-v1.0.2-beta1.i386.rpm  
filename is: linuxqq-v1.0.2-beta1.i386.rpm  
save to /tmp/down  
hostname = dl_dir.qq.com  
202.104.241.136  
file path is:/linuxqq/linuxqq-v1.0.2-beta1.i386.rpm  
the file data is 5046743  
how many parts do you want to download?  
5  
1009350 have downloaded!  
1009348 have downloaded!  
1009348 have downloaded!  
1009349 have downloaded!  
thread 1 finished!  
1009348 have downloaded!  
thread 2 finished!  
thread 3 finished!  
thread 4 finished!  
thread 5 finished!  
all download:5046743!  
open success!  
read 2 part  
open success!  
read 3 part  
open success!  
read 4 part  
open success!  
read 5 part  
!!!Hello World!!!



posted on 2014-04-17 22:18  犀利小猫  阅读(288)  评论(0编辑  收藏  举报