2009-07-11 14:51
LINUX C 编程实战 童永清 (著)
chapter 13 BT 下载软件的开发 //parse_metafile.c #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <malloc.h> #include <string.h> #include <time.h> #include "parse_metafile.h" #include "sha1.h" char metafile[] = "/home/dtdlut/linux_c_language/chapter13/bittorrent/test.torrent"; char *metafile_content = NULL; //保存种子文件的内容 int filesize; //种子文件的长度 int piece_length =0; //每个piece的长度,通常为256KB 即262144字节 char *pieces = NULL; //保存每个pieces的哈希值,每个哈希值为20字节 int pieces_length =0; //缓冲区pieces的长度 int multi_file =0; //指明是单文件还是多文件 char *file_name =NULL; //对于单文件,存放文件名;对于多文件,存放目录名 long long file_length =0; //存放待下载文件的总长度 Files *files_head = NULL; //只对多文件种子有效,存放各个文件的路径和长度 unsigned char info_hash[20]; //保存info_hash值,连接tracker和peer时使用 unsigned char peer_id[20] ; //保存peer_id的值,连接peer时使用 Announce_list *announce_list_head = NULL; #define DEBUG //功能:解析种子文件 //参数:metafile_name是种子文件名 //返回:处理成功返回0,否则返回-1 //附注:将种子文件的内容读到全局变量metafile_content所指向的缓冲区中以方便处理。实现代码为: int read_metafile(char *metafile_name) { long i; //以2进制、只读的方式打开文件 FILE *fp = fopen(metafile_name, "rb"); if(fp == NULL) { printf("%s:%d can not open file\n", __FILE__, __LINE__); return -1; } //获取种子文件的长度,filesize为全局变量,在parse_metafile.c头部定义 fseek(fp, 0, SEEK_END); filesize = ftell(fp); if(filesize == -1) { printf("%s:%d fseek failed \n", __FILE__, __LINE__); return -1; } metafile_content = (char *)malloc(filesize+1); if(metafile_content == NULL) { printf("%s:%d malloc failed\n", __FILE__, __LINE__); return -1; } fseek(fp, 0, SEEK_SET); for(i=0; i<filesize; i++) metafile_content[i] = fgetc(fp); fclose(fp); #ifdef DEBUG printf("metafile size is: %d\n", filesize); #endif return 0; } //功能:从种子文件中查找某个关键字 //参数:keyword为要查找的关键字,position用于返回关键字第一个字符所在的下标 //返回:成功执行并找到关键字返回1, 未找到返回0, 执行失败返回-1 int find_keyword(char *keyword, long *position) { long i; *position = -1; if(keyword == NULL) { return 0; } for(i=0; i<filesize-strlen(keyword); i++) { if(memcmp(&metafile_content[i], keyword, strlen(keyword)) == 0) { *position = i; return 1; } } return 0; } //功能:获取Tracker地址,并将获取的地址保存到全局变量announce_list_head指向的链表中 int read_announce_list() { Announce_list *node = NULL; Announce_list *p = NULL; int len = 0; long i; if( find_keyword("13:announce-list", &i) == 0) { if( find_keyword("8:announce", &i) == 1) { i = i+ strlen("8:announce"); while( isdigit(metafile_content[i]) ) { len = len *10 +(metafile_content[i] - '0'); i++; } i++; //跳过 ';' node = (Announce_list *) malloc(sizeof(Announce_list)); strncpy(node->announce, &metafile_content[i], len); node->announce[len] = '\0'; node->next = NULL; announce_list_head = node; } } else { //如果有13:announce-list关键词就不用处理8:announce关键词 i = i+ strlen("13:announce-list"); i++; //跳过'l' while(metafile_content[i] != 'e') { i++; //跳过'l' while( isdigit(metafile_content[i])) { len = len * 10 +(metafile_content[i] - '0'); i++; } if(metafile_content[i] == ':') i++; else return -1; //只处理以http开头的tracker地址,不处理以udp开头的地址 if(memcmp(&metafile_content[i], "http", 4) == 0) { node = (Announce_list*)malloc(sizeof(Announce_list)); strncpy(node->announce, &metafile_content[i], len); node->announce[len] = '\0'; node->next = NULL; if(announce_list_head == NULL) announce_list_head = node; else { p = announce_list_head; while(p->next != NULL) p = p->next; //使p指针指向最后那个结点 p->next = node; //node成为tracker列表的最后一个结点 } } i = i +len; len = 0; i++; //跳过'e' if( i >= filesize) return -1; }//while循环结束 } #ifdef DEBUG p = announce_list_head; // printf("ANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCE\n"); while(p != NULL) { printf("%s\n", p->announce); p = p->next; } //printf("ANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCEANNOUNCE\n"); #endif return 0; } //功能:连接某些tracker时会返回一个重定向的URL, 需要连接该URL才能获取Peer int add_an_annouce(char *url) { Announce_list *p = announce_list_head, *q; //若参数指定的URL在Tracker列表中已经存在,则无需添加 while(p != NULL) { if(strcmp(p->announce, url) == 0) break; p = p->next; } if( p != NULL) return 0; q = (Announce_list*)malloc(sizeof(Announce_list)); strcpy(q->announce, url); q->next = NULL; p = announce_list_head; if(p = NULL) { announce_list_head = q; return 1; } while( p->next != NULL) p = p->next; p->next = q; return 1; } //功能:判断是下载多个文件还是单文件,若含有关键字"5:files"则说明下载的是 //多个文件 int is_multi_files() { long i; if((find_keyword("5:files", &i) )== 1) { multi_file = 1; return 1; } #ifdef DEBUG // printf("IS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILES\n"); printf("is_multi_files: %d\n", multi_file); // printf("IS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILESIS_MULTI_FILES\n"); #endif return 0; } //功能:获取piece的长度 int get_piece_length() { long i; if(find_keyword("12:piece length", &i) == 1) { i = i+ strlen("12:piece length"); //跳过"12:piece length" i++; //跳过'i' while(metafile_content[i] != 'e') { piece_length = piece_length * 10 +(metafile_content[i]-'0'); i++; } } else return -1; #ifdef DEBUG //printf("PIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTH\n"); printf("piece length: %d\n", piece_length); //printf("PIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTHPIECE_LENGTH\n"); #endif return 0; } //功能:获取每个piece的hash值, 并保存到pieces所指向的缓冲区中 int get_pieces() { long i; if( find_keyword("6:pieces", &i) == 1) { i = i + strlen("6:pieces"); //跳过"6:pieces" while(metafile_content[i] != ':') { pieces_length = pieces_length * 10 + (metafile_content[i] - '0'); i++; } i++; //跳过':' pieces = (char*) malloc(pieces_length + 1); memcpy(pieces, &metafile_content[i], pieces_length); pieces[piece_length] = '\0'; } else return -1; #ifdef DEBUG //printf("GET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECES\n"); printf("get_pieces ok\n"); //printf("GET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECESGET_PIECES\n"); #endif return 0; } int get_file_name() { long i; int count = 0; if(find_keyword("4:name", &i) == 1) { i = i+6; while(metafile_content[i] != ':') { count = count * 10 + (metafile_content[i] - '0'); i++; } i++; file_name = (char *) malloc( count +1); memcpy(file_name, &metafile_content[i], count); file_name[count] = '\0'; } else { return -1; } #ifdef DEBUG // printf("\n COUNT is %ld\n", count); // printf("FILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAME\n"); printf("file_name:%s\n", file_name); // printf("FILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAMEFILE_NAME\n"); #endif return 0; } //功能: 获取待下载文件的长度 int get_file_length() { long i; if(is_multi_files() ==1 ) { if(files_head == NULL) { // printf("files_head is null\n"); get_files_length_path(); } Files *p = files_head; while(p != NULL) { file_length += p->length; p = p->next; } } else { // printf("is not multi files\n"); if( find_keyword("6:length", &i) == 1) { i = i+ strlen("6:length"); //跳过"6:length" i++; //跳过'i' while(metafile_content[i] != 'e') { file_length = file_length* 10 + (metafile_content[i] - '0'); i++; } } } #ifdef DEBUG // printf("FILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTH\n"); printf("file_length: %lld\n", file_length); // printf("FILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTHFILE_LENGTH\n"); #endif return 0; } //功能:对于多个文件,获取各个文件的路径以及长度 int get_files_length_path() { long i; unsigned long length; int count; Files *node = NULL; Files *p = NULL; if(is_multi_files() != 1) { // printf("NOT FILES\n"); return 0; } int num = 0; for(i=0; i<filesize - 8 ; i++) { if(memcmp(&metafile_content[i], "6:length", 8) == 0) { i= i+8; //跳过"6:length" i++; //跳过'i' length = 0; num++; while(metafile_content[i] != 'e') { length = length * 10 +(metafile_content[i]-'0'); i++; } // printf("length is %ld and num is %d\n", length, num); node = (Files*) malloc(sizeof(Files)); node->length = length; node->next = NULL; if(files_head == NULL) files_head =node; else { p = files_head; while(p->next) p = p->next; p->next = node; } } if(memcmp(&metafile_content[i], "4:path",6) == 0) { i = i+6; //跳过"4:path" i++; //跳过'l',由于是多文件肯定有多个文件路径 count = 0; while(metafile_content[i] != ':') { count = count * 10 + (metafile_content[i] - '0'); i++; } i++; //跳过':' p = files_head; while( p ->next != NULL) p = p->next; memcpy(p->path, &metafile_content[i], count); *(p->path + count) = '\0'; // printf("path is %s\n ", p->path); } } //printf("get file length path\n"); return 0; } //功能:计算info_hash的值 int get_info_hash() { int push_pop = 0; long i, begin, end; if(metafile_content == NULL) return -1; //begin的值表示的是关键字"4:info"对应值的起始下标 if( find_keyword("4:info", &i) == 1) begin = i+6; else return -1; i = i+6; //跳过"4:info" for(; i < filesize;) { if(metafile_content[i] == 'd') { push_pop++; i++; } else if(metafile_content[i] == 'l') { push_pop++; i++; } else if(metafile_content[i] == 'i') { i++; //跳过'i' if( i==filesize) return -1; while(metafile_content[i] != 'e') { if( (i+1) == filesize ) { printf("here 2\n"); return -1; } else i++; //继续判断下一个字符是否为'e' } i++; //跳过'e' } else if((metafile_content[i] >= '0') && ( metafile_content[i] <= '9')) { int number = 0; while((metafile_content[i] >= '0') && (metafile_content[i] <= '9')) { number = number * 10 + (metafile_content[i] - '0'); i++; } i++; //跳过 ':' i = i+ number; //跳过number个字符 } else if( metafile_content[i] == 'e') { push_pop--; if(push_pop == 0) { end = i; break; } else i++; } else return -1; } if( i == filesize) return -1; SHA1_CTX context; SHA1Init(&context); SHA1Update(&context, &metafile_content[begin], end-begin+1); SHA1Final(info_hash, &context); #ifdef DEBUG printf("info_hash:"); for(i=0; i<20;i++) printf("%.2x ", info_hash[i]); printf("\n"); #endif return 0; } //功能:生成一个唯一的peer id // int get_peer_id() { //设置产生随机数的种子 srand(time(NULL)); //使用rand函数生成一个随机数,并使用该随机数来构造peer_id //peer_id前8位固定为-TT1000- sprintf(peer_id, "-TT1000-%12d", rand()); #ifdef DEBUG printf("peer_id %s\n", peer_id); #endif return 0; } //功能:释放动态申请的内存 void release_memory_in_parse_metafile() { Announce_list *p; Files *q; if(metafile_content != NULL) free(metafile_content); if(file_name != NULL) free(file_name); if(pieces != NULL) free(pieces); while(announce_list_head != NULL) { p = announce_list_head; announce_list_head = announce_list_head->next; free(p); } while( files_head != NULL) { q = files_head; files_head = files_head->next; free(q); } } //功能:调用parse_metafile.c 中定义的函数,完成解析种子文件。该函数由main调用 //返回:解析成功返回0,否则返回-1 int main() { int ret; //读取种子文件 ret = read_metafile(metafile); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //从种子文件中读取tracker服务器的地址 ret = read_announce_list(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //判断是否为多文件 ret = is_multi_files(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //获取每个piece的长度,一般为256KB ret = get_piece_length(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //读取每个piece的哈希值 ret = get_pieces(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //获取要下载的文件名,对于多文件的种子,获取的是目录名 ret = get_file_name(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //对于多文件的种子,获取各个待下载的文件路径和文件长度 ret = get_files_length_path(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } //获取待下载文件的总长度 ret = get_file_length(); if(ret < 0) { printf("%s:%d wrong\n", __FILE__, __LINE__); return -1; } //获得info_hash,生成peer_id ret = get_info_hash(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } ret = get_peer_id(); if(ret < 0) { printf("%s:%d wrong", __FILE__, __LINE__); return -1; } return 0; } //parse_metafile.h //parse_metafile.h #ifndef PARSE_METAFILE #define PARSE_METAFILE //保存从种子文件中获得的tracker的URL typedef struct _Announce_list { char announce[128]; struct _Announce_list *next; }Announce_list; //保存各个待下载文件的路径和长度 typedef struct _Files { char path[256]; long length; struct _Files *next; }Files; int read_metafile(char *metafile_name); //读取种子文件 int find_keyword (char *keyword, long *position); //在种子文件中查找某个关键词 int read_announce_list(); //获取各个tracker服务器的地址 int add_an_announce(char *url); //向tracker列表添加一个URL int get_piece_length(); //获取每个piece的长度,一般为256KB int get_pieces(); //获取各个piece的哈希值 int is_multi_files(); //判断下载的是单个文件还是多个文件 int get_file_name(); //获取文件名,对于多文件,获取的是目录名 int get_file_length(); //获取下载文件的总长度 int get_files_length_path(); //获取文件的路径和长度,对多文件种子有效 int get_info_hash(); //由info关键词对应的值计算info_hash int get_peer_id(); //生成peer_id,每个peer都有一个20字节的peer_id void release_memory_in_parse_metafile(); //释放parse_metafile.c中动态分配的内存 int parse_metafile(char *metafile); //调用本文件中定义的函数,完成解析种子文件 #endif |